#include "mboard.h"
#include "mboard_private.h"
#include "sdio_hw.h"
#include "gpio_hw.h"
#include "dma_hw.h"
#include "rcc.h"
#include "nvic.h"
#include "mdebug.h"
#include "mgdb.h"
#include <string.h>
#include "mlog.h"
/* FatFs文件系统 */
#include "ff.h"
#include "diskio.h"

#undef MLOG_LEVEL_CUR
#define MLOG_LEVEL_CUR MLOG_LEVEL_OFF

/**
 * SDIO用作SD卡读写，D0/1/2/3/CMD板载了47K上拉电阻，就不需要片内上拉了
 *    PC8 --- SDIO_D0 --- MISO
 *    PC9 --- SDIO_D1 --- 外部上拉
 *    DC10 -- SDIO_D2 --- 外部上拉
 *    DC11 -- SDIO_D3 --- CS，卡片的该引脚有50K上拉电阻，SPI模式时用作片选功能，可用于卡片插入检测，可通过ACMD42禁用该上拉电阻。SD上电默认为SD接口协议，收到CMD0时若检测到该引脚被拉低，则切换到SPI模式
 *    PC12 -- SDIO_CK --- CLK
 *    PD2 --- SDIO_CMD -- MOSI
 */

/**
  * @brief SDMMC Commands Index
  */
/* 用于复位MMC/SD卡 */
#define SDIO_CMD_GO_IDLE_STATE                0U    /*!< Resets the SD memory card.                                                               */
#define SDIO_CMD_SEND_OP_COND                 1U    /*!< Sends host capacity support information and activates the card's initialization process. */
#define SDIO_CMD_ALL_SEND_CID                 2U    /*!< Asks any card connected to the host to send the CID numbers on the CMD line.             */
#define SDIO_CMD_SET_REL_ADDR                 3U    /*!< Asks the card to publish a new relative address (RCA).                                   */
#define SDIO_CMD_SET_DSR                      4U    /*!< Programs the DSR of all cards.                                                           */
/* 用于SDIO卡 */
#define SDIO_CMD_SDMMC_SEN_OP_COND            5U    /*!< Sends host capacity support information (HCS) and asks the accessed card to send its
                                                          operating condition register (OCR) content in the response on the CMD line.              */
#define SDIO_CMD_HS_SWITCH                    6U    /*!< Checks switchable function (mode 0) and switch card function (mode 1).                   */
#define SDIO_CMD_SEL_DESEL_CARD               7U    /*!< Selects the card by its own relative address and gets deselected by any other address    */
#define SDIO_CMD_HS_SEND_EXT_CSD              8U    /*!< Sends SD Memory Card interface condition, which includes host supply voltage information
                                                          and asks the card whether card supports voltage.                                         */
#define SDIO_CMD_SEND_CSD                     9U    /*!< Addressed card sends its card specific data (CSD) on the CMD line.                       */
#define SDIO_CMD_SEND_CID                     10U   /*!< Addressed card sends its card identification (CID) on the CMD line.                      */
#define SDIO_CMD_READ_DAT_UNTIL_STOP          11U   /*!< SD card doesn't support it.                                                              */
#define SDIO_CMD_STOP_TRANSMISSION            12U   /*!< Forces the card to stop transmission.                                                    */
#define SDIO_CMD_SEND_STATUS                  13U   /*!< Addressed card sends its status register.                                                */
#define SDIO_CMD_HS_BUSTEST_READ              14U   /*!< Reserved                                                                                 */
#define SDIO_CMD_GO_INACTIVE_STATE            15U   /*!< Sends an addressed card into the inactive state.                                         */
#define SDIO_CMD_SET_BLOCKLEN                 16U   /*!< Sets the block length (in bytes for SDSC) for all following block commands
                                                          (read, write, lock). Default block length is fixed to 512 Bytes. Not effective
                                                          for SDHS and SDXC.                                                                       */
#define SDIO_CMD_READ_SINGLE_BLOCK            17U   /*!< Reads single block of size selected by SET_BLOCKLEN in case of SDSC, and a block of
                                                          fixed 512 bytes in case of SDHC and SDXC.                                                */
#define SDIO_CMD_READ_MULT_BLOCK              18U   /*!< Continuously transfers data blocks from card to host until interrupted by
                                                          STOP_TRANSMISSION command.                                                               */
#define SDIO_CMD_HS_BUSTEST_WRITE             19U   /*!< 64 bytes tuning pattern is sent for SDR50 and SDR104.                                    */
#define SDIO_CMD_WRITE_DAT_UNTIL_STOP         20U   /*!< Speed class control command.                                                             */
#define SDIO_CMD_SET_BLOCK_COUNT              23U   /*!< Specify block count for CMD18 and CMD25.                                                 */
#define SDIO_CMD_WRITE_SINGLE_BLOCK           24U   /*!< Writes single block of size selected by SET_BLOCKLEN in case of SDSC, and a block of
                                                          fixed 512 bytes in case of SDHC and SDXC.                                                */
#define SDIO_CMD_WRITE_MULT_BLOCK             25U   /*!< Continuously writes blocks of data until a STOP_TRANSMISSION follows.                    */
#define SDIO_CMD_PROG_CID                     26U   /*!< Reserved for manufacturers.                                                              */
#define SDIO_CMD_PROG_CSD                     27U   /*!< Programming of the programmable bits of the CSD.                                         */
#define SDIO_CMD_SET_WRITE_PROT               28U   /*!< Sets the write protection bit of the addressed group.                                    */
#define SDIO_CMD_CLR_WRITE_PROT               29U   /*!< Clears the write protection bit of the addressed group.                                  */
#define SDIO_CMD_SEND_WRITE_PROT              30U   /*!< Asks the card to send the status of the write protection bits.                           */
#define SDIO_CMD_SD_ERASE_GRP_START           32U   /*!< Sets the address of the first write block to be erased. (For SD card only).              */
#define SDIO_CMD_SD_ERASE_GRP_END             33U   /*!< Sets the address of the last write block of the continuous range to be erased.           */
#define SDIO_CMD_ERASE_GRP_START              35U   /*!< Sets the address of the first write block to be erased. Reserved for each command
                                                          system set by switch function command (CMD6).                                            */
#define SDIO_CMD_ERASE_GRP_END                36U   /*!< Sets the address of the last write block of the continuous range to be erased.
                                                          Reserved for each command system set by switch function command (CMD6).                  */
#define SDIO_CMD_ERASE                        38U   /*!< Reserved for SD security applications.                                                   */
#define SDIO_CMD_FAST_IO                      39U   /*!< SD card doesn't support it (Reserved).                                                   */
#define SDIO_CMD_GO_IRQ_STATE                 40U   /*!< SD card doesn't support it (Reserved).                                                   */
#define SDIO_CMD_LOCK_UNLOCK                  42U   /*!< Sets/resets the password or lock/unlock the card. The size of the data block is set by
                                                          the SET_BLOCK_LEN command.                                                               */
#define SDIO_CMD_APP_CMD                      55U   /*!< Indicates to the card that the next command is an application specific command rather
                                                          than a standard command.                                                                 */
#define SDIO_CMD_GEN_CMD                      56U   /*!< Used either to transfer a data block to the card or to get a data block from the card
                                                          for general purpose/application specific commands.                                       */
#define SDIO_CMD_NO_CMD                       64U   /*!< No command                                                                               */

/**
  * @brief Following commands are SD Card Specific commands.
  *        SDMMC_APP_CMD should be sent before sending these commands.
  */
#define SDIO_CMD_APP_SD_SET_BUSWIDTH          6U    /*!< (ACMD6) Defines the data bus width to be used for data transfer. The allowed data bus
                                                           widths are given in SCR register.                                                       */
#define SDIO_CMD_SD_APP_STATUS                13U   /*!< (ACMD13) Sends the SD status.                                                            */
#define SDIO_CMD_SD_APP_SEND_NUM_WRITE_BLOCKS 22U   /*!< (ACMD22) Sends the number of the written (without errors) write blocks. Responds with
                                                          32bit+CRC data block.                                                                    */
/* SD卡使用该命令进行识别，发送该命令前需要先发送ACMD55，MMC卡不响应ACMD55，
   此时可以发送CMD1，进而可以区分出MMC卡 */
#define SDIO_CMD_SD_APP_OP_COND               41U   /*!< (ACMD41) Sends host capacity support information (HCS) and asks the accessed card to
                                                          send its operating condition register (OCR) content in the response on the CMD line.     */
#define SDIO_CMD_SD_APP_SET_CLR_CARD_DETECT   42U   /*!< (ACMD42) Connect/Disconnect the 50 KOhm pull-up resistor on CD/DAT3 (pin 1) of the card  */
#define SDIO_CMD_SD_APP_SEND_SCR              51U   /*!< Reads the SD Configuration Register (SCR).                                               */
/* 用于复位SDIO卡 */
#define SDIO_CMD_SDMMC_RW_DIRECT              52U   /*!< For SD I/O card only, reserved for security specification.                               */
#define SDIO_CMD_SDMMC_RW_EXTENDED            53U   /*!< For SD I/O card only, reserved for security specification.                               */

/**
  * @brief Following commands are SD Card Specific security commands.
  *        SDIO_CMD_APP_CMD should be sent before sending these commands.
  */
#define SDIO_CMD_SD_APP_GET_MKB                     43U
#define SDIO_CMD_SD_APP_GET_MID                     44U
#define SDIO_CMD_SD_APP_SET_CER_RN1                 45U
#define SDIO_CMD_SD_APP_GET_CER_RN2                 46U
#define SDIO_CMD_SD_APP_SET_CER_RES2                47U
#define SDIO_CMD_SD_APP_GET_CER_RES1                48U
#define SDIO_CMD_SD_APP_SECURE_READ_MULTIPLE_BLOCK  18U
#define SDIO_CMD_SD_APP_SECURE_WRITE_MULTIPLE_BLOCK 25U
#define SDIO_CMD_SD_APP_SECURE_ERASE                38U
#define SDIO_CMD_SD_APP_CHANGE_SECURE_AREA          49U
#define SDIO_CMD_SD_APP_SECURE_WRITE_MKB            48U

/**
 * 卡状态寄存器(CSR, Card Status Register)，响应格式为R1，32bits
 */
#define SDIO_CSR_ADDR_OUT_OF_RANGE     (0x1U<<31) /* 读写命令地址参数超出了此卡允许的范围，读清 */
#define SDIO_CSR_ADDR_MISALIGNED       (0x1U<<30) /* 多块读写操作地址未对齐到块，读清 */
#define SDIO_CSR_BLOCK_LEN_ERR         (0x1U<<29) /* 可能是 SET_BLOCKLEN 命令的参数超过了卡的最大允许值，或者先前定义的块长度对当前命令而言非法，读清 */
#define SDIO_CSR_ERASE_SEQ_ERR         (0x1U<<28) /* 擦除命令序列中发生错误，读清*/
#define SDIO_CSR_BAD_ERASE_PARAM       (0x1U<<27) /* 为擦除选择的擦除组无效，读清 */
#define SDIO_CSR_WRITE_PROT_VIOLATION  (0x1U<<26) /* 尝试对写保护块进行编程，读清 */
#define SDIO_CSR_CARD_LOCKED           (0x1U<<25) /* 卡已由主机锁定，需要解锁操作清除 */
#define SDIO_CSR_LOCK_UNLOCK_FAILED    (0x1U<<24) /* 如果在锁定/解锁卡命令中检测到序列或密码错误，则会设置，读清 */
#define SDIO_CSR_COM_CRC_FAILED        (0x1U<<23) /* 先前命令的 CRC 校验失败，接收到有效命令后清除 */
#define SDIO_CSR_ILLEGAL_CMD           (0x1U<<22) /* 命令对于卡状态不合法，接收到有效命令后清除 */
#define SDIO_CSR_CARD_ECC_FAILED       (0x1U<<21) /* 应用了卡内部 ECC，但未能更正数据，读清 */
#define SDIO_CSR_CC_ERROR              (0x1U<<20) /* （标准中未定义）发生了卡错误，该错误与主机命令无关，读清 */
#define SDIO_CSR_GENERAL_UNKNOWN_ERROR (0x1U<<19) /* （标准中未定义）与最后一个主机命令的执行（以及在此期间的检测）有关的一般卡错误（例如，读取或写入失败），读清 */
#define SDIO_CSR_STREAM_READ_UNDERRUN  (0x1U<<18) /* SD卡保留 */
#define SDIO_CSR_STREAM_WRITE_OVERRUN  (0x1U<<17) /* SD卡保留 */
#define SDIO_CSR_CID_CSD_OVERWRITE     (0x1U<<16) /* CID 寄存器已写入且无法覆盖、CSD 的只读部分与卡内容不匹配、尝试恢复已设置的副本（设置为原始值），读清 */
#define SDIO_CSR_WP_ERASE_SKIP         (0x1U<<15) /* 仅当由于现有写入而导致部分地址空间被擦除时才会设置，读清 */
#define SDIO_CSR_CARD_ECC_DISABLED     (0x1U<<14) /* 命令已在未使用内部 ECC 的情况下执行，需要启用卡ECC功能的操作进行清除 */
#define SDIO_CSR_ERASE_RESET           (0x1U<<13) /* 收到的不是擦除序列命令，读清 */
#define SDIO_CSR_STATE_IDLE            (0x0U<<9)  /* 空闲 */
#define SDIO_CSR_STATE_READY           (0x1U<<9)  /* 就绪 */
#define SDIO_CSR_STATE_RECOGNITION     (0x2U<<9)  /* 识别，通过CMD7选中卡后，卡切换到传输模式 */
#define SDIO_CSR_STATE_STANDBY         (0x3U<<9)  /* 待机 */
#define SDIO_CSR_STATE_TRANSFER        (0x4U<<9)  /* 传输 */
#define SDIO_CSR_STATE_DATA            (0x5U<<9)  /* 发送，主机读数据 */
#define SDIO_CSR_STATE_RX              (0x6U<<9)  /* 接收，主机写数据 */
#define SDIO_CSR_STATE_PGM             (0x7U<<9)  /* 编程，卡接收到数据后开始编程 */
#define SDIO_CSR_STATE_DISCONNECT      (0x8U<<9)  /* 断开，卡处于编程状态时被取消选中 */
#define SDIO_CSR_STATE_BURST           (0x9U<<9)  /* SD卡保留 */
#define SDIO_CSR_STATE_MASK            (0xFU<<9)  /* 字段掩码 */
#define SDIO_CSR_READY_FOR_DATA        (0x1U<<8)  /* 对应于总线上的缓冲器空信令，0:忙 1:空闲 */
#define SDIO_CSR_SWITCH_ERROR          (0x1U<<7)  /* 卡不会根据 SWITCH 命令的请求切换到预期模式 */
#define SDIO_CSR_APP_CMD               (0x1U<<5)  /* 命令已解释为ACMD的指示 */
#define SDIO_CSR_AKE_SEQ_ERROR         (0x1U<<3)  /* 认证过程序列中出现错误 */
#define SDIO_CSR_ERRORBITS             0xFDFFE008U

/**
 * CMD3的响应格式为R6，其中[31:16]是RCA(相对地址)，[15:0]对应着CSR[23:22]
 * [19:19][12:0]
 */
#define SDIO_R6_CSR_COM_CRC_FAILED        (0x1U<<15) /* 先前命令的 CRC 校验失败，接收到有效命令后清除 */
#define SDIO_R6_CSR_ILLEGAL_CMD           (0x1U<<14) /* 命令对于卡状态不合法，接收到有效命令后清除 */
#define SDIO_R6_CSR_GENERAL_UNKNOWN_ERROR (0x1U<<13) /* （标准中未定义）与最后一个主机命令的执行（以及在此期间的检测）有关的一般卡错误（例如，读取或写入失败），读清 */
#define SDIO_R6_CSR_STATE_IDLE            (0x0U<<9)  /* 空闲 */
#define SDIO_R6_CSR_STATE_READY           (0x1U<<9)  /* 就绪 */
#define SDIO_R6_CSR_STATE_RECOGNITION     (0x2U<<9)  /* 识别，通过CMD7选中卡后，卡切换到传输模式 */
#define SDIO_R6_CSR_STATE_STANDBY         (0x3U<<9)  /* 待机 */
#define SDIO_R6_CSR_STATE_TRANSFER        (0x4U<<9)  /* 传输 */
#define SDIO_R6_CSR_STATE_DATA            (0x5U<<9)  /* 发送，主机读数据 */
#define SDIO_R6_CSR_STATE_RX              (0x6U<<9)  /* 接收，主机写数据 */
#define SDIO_R6_CSR_STATE_PGM             (0x7U<<9)  /* 编程，卡接收到数据后开始编程 */
#define SDIO_R6_CSR_STATE_DISCONNECT      (0x8U<<9)  /* 断开，卡处于编程状态时被取消选中 */
#define SDIO_R6_CSR_STATE_BURST           (0x9U<<9)  /* SD卡保留 */
#define SDIO_R6_CSR_READY_FOR_DATA        (0x1U<<8)  /* 对应于总线上的缓冲器空信令 */
#define SDIO_R6_CSR_SWITCH_ERROR          (0x1U<<7)  /* 卡不会根据 SWITCH 命令的请求切换到预期模式 */
#define SDIO_R6_CSR_APP_CMD               (0x1U<<5)  /* 命令已解释为ACMD的指示 */
#define SDIO_R6_CSR_AKE_SEQ_ERROR         (0x1U<<3)  /* 认证过程序列中出现错误 */
#define SDIO_R6_CSR_ERRORBITS             0xE008U


/**
 * 操作条件寄存器(OCR, Operation Conditions Register)，32bits，主要为卡的操作
 * 电压等信息，响应格式为R3，作为ACMD41的响应
 */
#define SDIO_OCR_READY_DIS             (0x0U<<31) /* 卡上电未完成 */
#define SDIO_OCR_READY_EN              (0x1U<<31) /* 卡上电已完成 */
#define SDIO_OCR_CCS_STD               (0x0U<<30) /* 标准卡，SDSC，小于2GB */
#define SDIO_OCR_CCS_HIGH              (0x1U<<30) /* 高容量卡，SDHC/SDXC */
#define SDIO_OCR_3_5V_3_6V             (0x1U<<23) /* 支持该范围电压 */
#define SDIO_OCR_3_4V_3_5V             (0x1U<<22)
#define SDIO_OCR_3_3V_3_4V             (0x1U<<21)
#define SDIO_OCR_3_2V_3_3V             (0x1U<<20)
#define SDIO_OCR_3_1V_3_2V             (0x1U<<19)
#define SDIO_OCR_3_0V_3_1V             (0x1U<<18)
#define SDIO_OCR_2_9V_3_0V             (0x1U<<17)
#define SDIO_OCR_2_8V_2_9V             (0x1U<<16)
#define SDIO_OCR_2_7V_2_8V             (0x1U<<15)
#define SDIO_OCR_LOW_VOLTAGE_DIS       (0x0U<<7) /* 不支持低电压 */
#define SDIO_OCR_LOW_VOLTAGE_EN        (0x1U<<7) /* 支持低电压，支持双电压的卡收到CMD8命令后会置起该位，否者该位为0，双电压的卡有两排引脚，低电压的卡支持更高的时钟频率 */

/**
 * 卡识别寄存器(CID, Card Identification Number)，128bits，提供制造商ID、
 * OEM/应用ID、产品名称、版本、序列号、制造日期等，响应格式为R2，作为CMD10
 * 的响应，该值通常由厂商写入，主机只读
 */
#define SDIO_CID_MID(cid)       SDIO_MASK_VAL(cid[0],31,24) /* [127:120][0,31:24] 8bits，制造商ID，二进制数，由SD卡协会管理和分配 */
#define SDIO_CID_OID0(cid)      SDIO_MASK_VAL(cid[0],23,16) /* [119:104][0,23:16] 16bits，OEM/应用ID，2字节ASCII，由制造商分配，通常为"SD" */
#define SDIO_CID_OID1(cid)      SDIO_MASK_VAL(cid[0],15, 8) /* [ 63: 60][0,15: 8] */
#define SDIO_CID_PNM0(cid)      SDIO_MASK_VAL(cid[0], 7, 0) /* [103: 96][0, 7: 0] 40bits，产品名称，5字节ASCII */
#define SDIO_CID_PNM1(cid)      SDIO_MASK_VAL(cid[1],31,24) /* [ 95: 88][1,31:24] */
#define SDIO_CID_PNM2(cid)      SDIO_MASK_VAL(cid[1],23,16) /* [ 87: 80][1,23:16] */
#define SDIO_CID_PNM3(cid)      SDIO_MASK_VAL(cid[1],15, 8) /* [ 79: 72][1,15: 8] */
#define SDIO_CID_PNM4(cid)      SDIO_MASK_VAL(cid[1], 7, 0) /* [ 71: 64][1, 7: 0] */
#define SDIO_CID_PRVH(cid)      SDIO_MASK_VAL(cid[2],31,28) /* [ 63: 60][2,31:28] 4bits，产品版本大号 */
#define SDIO_CID_PRVL(cid)      SDIO_MASK_VAL(cid[2],27,24) /* [ 59: 56][2,27:24] 4bits，产品版本小号 */
#define SDIO_CID_PSN(cid)       ((SDIO_MASK_VAL(cid[2],23,0)<<8)|SDIO_MASK_VAL(cid[3],31,24)) /* [ 55: 24][2,23: 0][3,31:24] 32bits，产品序列号，二进制数 */
#define SDIO_CID_reserved0(cid) SDIO_MASK_VAL(cid[3],23,20) /* [ 23: 20][3,23:20] 4bits，保留 */
#define SDIO_CID_MDTY(cid)      SDIO_MASK_VAL(cid[3],19,12) /* [ 19: 12][3,19:12] 8bits，生产日期年份，1代表2000年 */
#define SDIO_CID_MDTM(cid)      SDIO_MASK_VAL(cid[3],11, 8) /* [ 11:  8][3,11: 8] 4bits，生产日期月份，1代表1月份 */
#define SDIO_CID_CRC(cid)       SDIO_MASK_VAL(cid[3],7,  1) /* [  7:  1][3, 7: 1] 7bits，CRC校验值 */
#define SDIO_CID_reserved1(cid) SDIO_MASK_VAL(cid[3],0,  0) /* [  0:  0][3, 0: 0] 1bits，保留为1 */

/**
 * 特定数据寄存器(CSD, Card Specific Data)，128bits，响应格式为R2，
 * 使用CMD9读取，使用CMD27写入，写入时只读的部分必须保持一致
 */
#define SDIO_CSD_STRUCT_SDSC               0x0  /* SD V2.0协议中，CSD结构体有两套，其中标准容量卡和SD V1.0的结构体兼容 */
#define SDIO_CSD_STRUCT_SDHC_SDXC          0x1  /* 大容量卡结构体，大于2GB */
#define SDIO_CSD_V1_TAAC_MULT_1_0          0x1  /* 数据访问时间倍数1.0 */
#define SDIO_CSD_V1_TAAC_MULT_1_2          0x2
#define SDIO_CSD_V1_TAAC_MULT_1_3          0x3
#define SDIO_CSD_V1_TAAC_MULT_1_5          0x4
#define SDIO_CSD_V1_TAAC_MULT_2_0          0x5
#define SDIO_CSD_V1_TAAC_MULT_2_5          0x6
#define SDIO_CSD_V1_TAAC_MULT_3_0          0x7
#define SDIO_CSD_V1_TAAC_MULT_3_5          0x8
#define SDIO_CSD_V1_TAAC_MULT_4_0          0x9
#define SDIO_CSD_V1_TAAC_MULT_4_5          0xA
#define SDIO_CSD_V1_TAAC_MULT_5_0          0xB
#define SDIO_CSD_V1_TAAC_MULT_5_5          0xC
#define SDIO_CSD_V1_TAAC_MULT_6_0          0xD
#define SDIO_CSD_V1_TAAC_MULT_7_0          0xE
#define SDIO_CSD_V1_TAAC_MULT_8_0          0xF
#define SDIO_CSD_V1_TAAC_UNIT_1NS          0x0  /* 数据访问时间单位1ns */
#define SDIO_CSD_V1_TAAC_UNIT_10NS         0x1
#define SDIO_CSD_V1_TAAC_UNIT_100NS        0x2
#define SDIO_CSD_V1_TAAC_UNIT_1US          0x3
#define SDIO_CSD_V1_TAAC_UNIT_10US         0x4
#define SDIO_CSD_V1_TAAC_UNIT_100US        0x5
#define SDIO_CSD_V1_TAAC_UNIT_1MS          0x6
#define SDIO_CSD_V1_TAAC_UNIT_10MS         0x7
#define SDIO_CSD_V1_NSAC_CYCLES(nasc)      ((nasc)*100) /* 最差的等待时间，以SDIO的输出时钟计算，单位是100个周期，实际计算访问超时时间是将NASC和TAAC加起来 */
#define SDIO_CSD_V1_TRAN_SPEED_MULT_1_0    0x1  /* 通信速率倍数1.0 */
#define SDIO_CSD_V1_TRAN_SPEED_MULT_1_2    0x2
#define SDIO_CSD_V1_TRAN_SPEED_MULT_1_3    0x3
#define SDIO_CSD_V1_TRAN_SPEED_MULT_1_5    0x4
#define SDIO_CSD_V1_TRAN_SPEED_MULT_2_0    0x5
#define SDIO_CSD_V1_TRAN_SPEED_MULT_2_5    0x6  /* 标准卡，最大25M */
#define SDIO_CSD_V1_TRAN_SPEED_MULT_3_0    0x7
#define SDIO_CSD_V1_TRAN_SPEED_MULT_3_5    0x8
#define SDIO_CSD_V1_TRAN_SPEED_MULT_4_0    0x9
#define SDIO_CSD_V1_TRAN_SPEED_MULT_4_5    0xA
#define SDIO_CSD_V1_TRAN_SPEED_MULT_5_0    0xB  /* 高速卡，最大50M，通过CMD6/CMD0切回到默认值时，该值会重新设置为0x6 */
#define SDIO_CSD_V1_TRAN_SPEED_MULT_5_5    0xC
#define SDIO_CSD_V1_TRAN_SPEED_MULT_6_0    0xD
#define SDIO_CSD_V1_TRAN_SPEED_MULT_7_0    0xE
#define SDIO_CSD_V1_TRAN_SPEED_MULT_8_0    0xF
#define SDIO_CSD_V1_TRAN_SPEED_UNIT_100K   0x0  /* 通信速率单位100Kbps */
#define SDIO_CSD_V1_TRAN_SPEED_UNIT_1M     0x1
#define SDIO_CSD_V1_TRAN_SPEED_UNIT_10M    0x2  /* 标准卡/高速卡的单位 */
#define SDIO_CSD_V1_TRAN_SPEED_UNIT_100M   0x3
#define SDIO_CSD_V1_CCC_CLASE0             (0x1<<0)  /* 基本命令集，CMD0/1/8/9/10/12/13，固定为01x110110101 */
#define SDIO_CSD_V1_CCC_CLASE1             (0x1<<1)
#define SDIO_CSD_V1_CCC_CLASE2             (0x1<<2)  /* 块读命令集，CMD16/17/18 */
#define SDIO_CSD_V1_CCC_CLASE3             (0x1<<3)
#define SDIO_CSD_V1_CCC_CLASE4             (0x1<<4)  /* 块写命令集，CMD16/24/25/27 */
#define SDIO_CSD_V1_CCC_CLASE5             (0x1<<5)  /* 擦除命令集，CMD32/33/38 */
#define SDIO_CSD_V1_CCC_CLASE6             (0x1<<6)  /* 写保护命令集，CMD28/29/30 */
#define SDIO_CSD_V1_CCC_CLASE7             (0x1<<7)  /* 锁卡命令集，CMD16/42 */
#define SDIO_CSD_V1_CCC_CLASE8             (0x1<<8)  /* 专用应用命令集，CMD55/56 */
#define SDIO_CSD_V1_CCC_CLASE9             (0x1<<9)  /* IO模式命令集，CMD5/52/53 */
#define SDIO_CSD_V1_CCC_CLASE10            (0x1<<10) /* 模式切换命令集，CMD6/34/35/36/37/50/57 */
#define SDIO_CSD_V1_CCC_CLASE11            (0x1<<11)
#define SDIO_CSD_V1_READ_BL_LEN_V(val)     (0x1U<<(val))  /* 数据块的字节数，读和写的块长度应该是一样的 */
#define SDIO_CSD_V1_READ_BL_PARTIAL_DIS    0x0  /* 表示只能按块读取，不能读部分 */
#define SDIO_CSD_V1_READ_BL_PARTIAL_EN     0x1  /* 表示可以不整块读取，默认值 */
#define SDIO_CSD_V1_WRITE_BLK_MISALIGN_DIS 0x0
#define SDIO_CSD_V1_WRITE_BLK_MISALIGN_EN  0x0  /* 写操作允许跨越块边界 */
#define SDIO_CSD_V1_READ_BLK_MISALIGN_DIS  0x0
#define SDIO_CSD_V1_READ_BLK_MISALIGN_EN   0x0  /* 读操作允许跨越块边界 */
#define SDIO_CSD_V1_DSR_IMP_DIS            0x0  /* 卡未实现DSR(驱动配置)寄存器 */
#define SDIO_CSD_V1_DSR_IMP_EN             0x1  /* 可使用DSR */
/* 此处计算的容量不包含卡的安全分区 */
#define _SDIO_CsdV1BlockLen(READ_BL_LEN)   SDIO_CSD_V1_READ_BL_LEN_V(READ_BL_LEN) /* 每个块的字节数 */
#define _SDIO_CsdV1BlockCount(C_SIZE, C_SIZE_MULT)   (((C_SIZE)+1)*(0x1U<<((C_SIZE_MULT)+2)))  /* 有多少可用块数 */
#define SDIO_CSD_V1_VDD_x_CURR_MIN_0_5MA   0x0  /* 读写的最小电流0.5mA */
#define SDIO_CSD_V1_VDD_x_CURR_MIN_1MA     0x1
#define SDIO_CSD_V1_VDD_x_CURR_MIN_5MA     0x2
#define SDIO_CSD_V1_VDD_x_CURR_MIN_10MA    0x3
#define SDIO_CSD_V1_VDD_x_CURR_MIN_25MA    0x4
#define SDIO_CSD_V1_VDD_x_CURR_MIN_35MA    0x5
#define SDIO_CSD_V1_VDD_x_CURR_MIN_60MA    0x6
#define SDIO_CSD_V1_VDD_x_CURR_MIN_100MA   0x7
#define SDIO_CSD_V1_VDD_x_CURR_MAX_1MA     0x0  /* 读写的最大电流1mA */
#define SDIO_CSD_V1_VDD_x_CURR_MAX_5MA     0x1
#define SDIO_CSD_V1_VDD_x_CURR_MAX_10MA    0x2
#define SDIO_CSD_V1_VDD_x_CURR_MAX_25MA    0x3
#define SDIO_CSD_V1_VDD_x_CURR_MAX_35MA    0x4
#define SDIO_CSD_V1_VDD_x_CURR_MAX_45MA    0x5
#define SDIO_CSD_V1_VDD_x_CURR_MAX_80MA    0x6
#define SDIO_CSD_V1_VDD_x_CURR_MAX_200MA   0x7
#define SDIO_CSD_V1_ERASE_BLK_EN_DIS       0x0  /* 表示主机只能按可扇区擦除 */
#define SDIO_CSD_V1_ERASE_BLK_EN_EN        0x1  /* 表示主机可按块擦除 */
#define SDIO_CSD_V1_SECTOR_SIZE_V(val)     ((val)+1)  /* 一个扇区包含多少个块 */
#define SDIO_CSD_V1_WP_GRP_SIZE_V(val)     ((val)+1)  /* 一个写保护组有多少个扇区 */
#define SDIO_CSD_V1_WP_GRP_ENABLE_DIS      0x0
#define SDIO_CSD_V1_WP_GRP_ENABLE_EN       0x1  /* 支持写保护组 */
#define SDIO_CSD_V1_R2W_FACTOR_V(val)      (0x1U<<(val))  /* 写超时=读时间*R2W_FACTOR，但主机不应该使用该参数，而应该固定为250ms */
#define SDIO_CSD_V1_WRITE_BL_LEN_V(val)    (0x1U<<(val))  /* 每个块的字节数，和READ_BL_LEN一致 */
#define SDIO_CSD_V1_WRITE_BL_PARTIAL_DIS   0x0  /* 只能按块写入，不能写部分 */
#define SDIO_CSD_V1_WRITE_BL_PARTIAL_EN    0x1  /* 可以按字节写入 */
#define SDIO_CSD_V1_FILE_FORMAT_GRP_DIS    0x0  /* FILE_FORMAT内容无效 */
#define SDIO_CSD_V1_FILE_FORMAT_GRP_EN     0x0  /* FILE_FORMAT内容有效，该值允许写1次，主要用于ROM类型的磁盘 */
#define SDIO_CSD_V1_COPY_NO                0x0  /* 表示卡的内容是原始的，仅允许写1次 */
#define SDIO_CSD_V1_COPY_YES               0x1  /* 表示卡的内容是拷贝的，常见于OTP/MTP设备 */
#define SDIO_CSD_V1_PERM_WRITE_PROTECT_DIS 0x0  /* 非永久写保护，仅允许写1次 */
#define SDIO_CSD_V1_PERM_WRITE_PROTECT_EN  0x1  /* 永久保护，不允许写或擦除 */
#define SDIO_CSD_V1_TMP_WRITE_PROTECT_DIS  0x0  /* 未开启写保护，可多次擦写 */
#define SDIO_CSD_V1_TMP_WRITE_PROTECT_EN   0x1  /* 临时写保护，该值可以设置和复位 */
#define SDIO_CSD_V1_FILE_FORMAT_HARD_DISK  0x0  /* 硬盘类似的文件系统，带有分区表，该值允许写1次 */
#define SDIO_CSD_V1_FILE_FORMAT_DOS_FAT    0x1  /* 带有启动扇区的DOS FAT类似的文件系统，无分区表 */
#define SDIO_CSD_V1_FILE_FORMAT_UNIVERSAL  0x2  /* 通用文件系统 */
#define SDIO_CSD_V1_FILE_FORMAT_UNKNOWN    0x3  /* 未知文件系统 */

#define SDIO_CSD_V2_TAAC_MULT_1_0          0x1  /* 固定为该值，数据访问时间倍数1.0，CSD V2.0版本下读取时间应固定为100ms，写时间固定为250ms */
#define SDIO_CSD_V2_TAAC_UNIT_1MS          0x6  /* 固定为该值，数据访问时间单位1ms */
#define SDIO_CSD_V2_NSAC_CYCLES            0x0  /* 固定为0 */
#define SDIO_CSD_V2_TRAN_SPEED_MULT_1_0    0x1  /* 通信速率倍数1.0 */
#define SDIO_CSD_V2_TRAN_SPEED_MULT_1_2    0x2
#define SDIO_CSD_V2_TRAN_SPEED_MULT_1_3    0x3
#define SDIO_CSD_V2_TRAN_SPEED_MULT_1_5    0x4
#define SDIO_CSD_V2_TRAN_SPEED_MULT_2_0    0x5
#define SDIO_CSD_V2_TRAN_SPEED_MULT_2_5    0x6  /* 标准卡，最大25M */
#define SDIO_CSD_V2_TRAN_SPEED_MULT_3_0    0x7
#define SDIO_CSD_V2_TRAN_SPEED_MULT_3_5    0x8
#define SDIO_CSD_V2_TRAN_SPEED_MULT_4_0    0x9
#define SDIO_CSD_V2_TRAN_SPEED_MULT_4_5    0xA
#define SDIO_CSD_V2_TRAN_SPEED_MULT_5_0    0xB  /* 高速卡，最大50M，通过CMD6/CMD0切回到默认值时，该值会重新设置为0x6 */
#define SDIO_CSD_V2_TRAN_SPEED_MULT_5_5    0xC
#define SDIO_CSD_V2_TRAN_SPEED_MULT_6_0    0xD
#define SDIO_CSD_V2_TRAN_SPEED_MULT_7_0    0xE
#define SDIO_CSD_V2_TRAN_SPEED_MULT_8_0    0xF
#define SDIO_CSD_V2_TRAN_SPEED_UNIT_100K   0x0  /* 通信速率单位100Kbps */
#define SDIO_CSD_V2_TRAN_SPEED_UNIT_1M     0x1
#define SDIO_CSD_V2_TRAN_SPEED_UNIT_10M    0x2  /* 标准卡/高速卡的单位 */
#define SDIO_CSD_V2_TRAN_SPEED_UNIT_100M   0x3
#define SDIO_CSD_V2_CCC_CLASE0             (0x1<<0)  /* 基本命令集，CMD0/1/8/9/10/12/13，固定为01x110110101 */
#define SDIO_CSD_V2_CCC_CLASE1             (0x1<<1)
#define SDIO_CSD_V2_CCC_CLASE2             (0x1<<2)  /* 块读命令集，CMD16/17/18 */
#define SDIO_CSD_V2_CCC_CLASE3             (0x1<<3)
#define SDIO_CSD_V2_CCC_CLASE4             (0x1<<4)  /* 块写命令集，CMD16/24/25/27 */
#define SDIO_CSD_V2_CCC_CLASE5             (0x1<<5)  /* 擦除命令集，CMD32/33/38 */
#define SDIO_CSD_V2_CCC_CLASE6             (0x1<<6)  /* 写保护命令集，CMD28/29/30 */
#define SDIO_CSD_V2_CCC_CLASE7             (0x1<<7)  /* 锁卡命令集，CMD16/42 */
#define SDIO_CSD_V2_CCC_CLASE8             (0x1<<8)  /* 专用应用命令集，CMD55/56 */
#define SDIO_CSD_V2_CCC_CLASE9             (0x1<<9)  /* IO模式命令集，CMD5/52/53 */
#define SDIO_CSD_V2_CCC_CLASE10            (0x1<<10) /* 模式切换命令集，CMD6/34/35/36/37/50/57 */
#define SDIO_CSD_V2_CCC_CLASE11            (0x1<<11)
#define SDIO_CSD_V2_READ_BL_LEN_512B       0x9  /* 固定为0x9，表示每个块512字节 */
#define SDIO_CSD_V2_READ_BL_PARTIAL_DIS    0x0  /* 固定为0，表示只能按块读取，不能读部分 */
#define SDIO_CSD_V2_WRITE_BLK_MISALIGN_DIS 0x0  /* 固定为0，表示不允许跨越块边界 */
#define SDIO_CSD_V2_READ_BLK_MISALIGN_DIS  0x0  /* 固定为0，表示不允许跨越块边界 */
#define SDIO_CSD_V2_DSR_IMP_DIS            0x0  /* 卡未实现DSR(驱动配置)寄存器 */
#define SDIO_CSD_V2_DSR_IMP_EN             0x1  /* 可使用DSR */
/* 此处计算的容量不包含卡的安全分区 */
#define _SDIO_CsdV2BlockLen()              512  /* 固定为512每个块的字节数 */
#define _SDIO_CsdV2BlockCount(C_SIZE)      (((C_SIZE)+1)*1024)  /* 有多少可用块数 */
#define SDIO_CSD_V2_ERASE_BLK_EN_EN        0x1   /* 固定为1，表示主机可按块擦除 */
#define SDIO_CSD_V2_SECTOR_SIZE_64         0x7F  /* 固定为0x7F，V2.0使用AU尺寸来表明存储边界，不使用该值 */
#define SDIO_CSD_V2_WP_GRP_SIZE_0          0x0  /* 固定为0，不支持写保护组 */
#define SDIO_CSD_V2_WP_GRP_ENABLE_DIS      0x0  /* 固定为0，不支持写保护组 */
#define SDIO_CSD_V2_R2W_FACTOR_4           0x2  /* 固定为0x2，写超时=读时间*R2W_FACTOR，但主机不应该使用该参数，而应该固定为250ms */
#define SDIO_CSD_V2_WRITE_BL_LEN_512B      0x9  /* 固定为0x9，每个块的字节数，和READ_BL_LEN一致 */
#define SDIO_CSD_V2_WRITE_BL_PARTIAL_DIS   0x0  /* 固定为0，只能按块写入，不能写部分 */
#define SDIO_CSD_V2_FILE_FORMAT_GRP_DIS    0x0  /* 固定为0，FILE_FORMAT内容无效 */
#define SDIO_CSD_V2_COPY_NO                0x0  /* 表示卡的内容是原始的，仅允许写1次 */
#define SDIO_CSD_V2_COPY_YES               0x1  /* 表示卡的内容是拷贝的，常见于OTP/MTP设备 */
#define SDIO_CSD_V2_PERM_WRITE_PROTECT_DIS 0x0  /* 非永久写保护，仅允许写1次 */
#define SDIO_CSD_V2_PERM_WRITE_PROTECT_EN  0x1  /* 永久保护，不允许写或擦除 */
#define SDIO_CSD_V2_TMP_WRITE_PROTECT_DIS  0x0  /* 未开启写保护，可多次擦写 */
#define SDIO_CSD_V2_TMP_WRITE_PROTECT_EN   0x1  /* 临时写保护，该值可以设置和复位 */
#define SDIO_CSD_V2_FILE_FORMAT_NONE       0x0  /* 固定为0，无意义 */

#define SDIO_CSD_STRUCT(csd)                   SDIO_MASK_VAL(csd[0], 31, 30) /* [127:126][0,31:30] */
#define SDIO_CSD_V1_reserved0(csd)             SDIO_MASK_VAL(csd[0], 29, 24) /* [125:120][0,29:24] */
#define SDIO_CSD_V1_TAAC_MULT(csd)             SDIO_MASK_VAL(csd[0], 23, 19) /* [119:115][0,23:19] */
#define SDIO_CSD_V1_TAAC_UNIT(csd)             SDIO_MASK_VAL(csd[0], 18, 16) /* [114:112][0,18:16] */
#define SDIO_CSD_V1_NSAC(csd)                  SDIO_MASK_VAL(csd[0], 15,  8) /* [111:104][0,15: 8] */
#define SDIO_CSD_V1_TRAN_SPEED_MULT(csd)       SDIO_MASK_VAL(csd[0],  7,  3) /* [103: 99][0, 7: 3] */
#define SDIO_CSD_V1_TRAN_SPEED_UNIT(csd)       SDIO_MASK_VAL(csd[0],  2,  0) /* [ 98: 96][0, 2: 0] */
#define SDIO_CSD_V1_CCC(csd)                   SDIO_MASK_VAL(csd[1], 31, 20) /* [ 95: 84][1,31:20] */
#define SDIO_CSD_V1_READ_BL_LEN(csd)           SDIO_MASK_VAL(csd[1], 19, 16) /* [ 83: 80][1,19:16] */
#define SDIO_CSD_V1_READ_BL_PARTIAL(csd)       SDIO_MASK_VAL(csd[1], 15, 15) /* [ 79: 79][1,15:15] */
#define SDIO_CSD_V1_WRITE_BLK_MISALIGN(csd)    SDIO_MASK_VAL(csd[1], 14, 14) /* [ 78: 78][1,14:14] */
#define SDIO_CSD_V1_READ_BLK_MISALIGN(csd)     SDIO_MASK_VAL(csd[1], 13, 13) /* [ 77: 77][1,13:13] */
#define SDIO_CSD_V1_DSR_IMP(csd)               SDIO_MASK_VAL(csd[1], 12, 12) /* [ 76: 76][1,12:12] */
#define SDIO_CSD_V1_reserved1(csd)             SDIO_MASK_VAL(csd[1], 11, 10) /* [ 75: 74][1,11: 10] */
#define SDIO_CSD_V1_C_SIZE(csd)                ((SDIO_MASK_VAL(csd[1],  9,  0)<<2)|SDIO_MASK_VAL(csd[2], 31, 30)) /* [ 73: 62][1, 9: 0][2,31:30] */
#define SDIO_CSD_V1_VDD_R_CURR_MIN(csd)        SDIO_MASK_VAL(csd[2], 29, 27) /* [ 61: 59][2,29:27] */
#define SDIO_CSD_V1_VDD_R_CURR_MAX(csd)        SDIO_MASK_VAL(csd[2], 26, 24) /* [ 58: 56][2,26:24] */
#define SDIO_CSD_V1_VDD_W_CURR_MIN(csd)        SDIO_MASK_VAL(csd[2], 23, 21) /* [ 55: 53][2,23:21] */
#define SDIO_CSD_V1_VDD_W_CURR_MAX(csd)        SDIO_MASK_VAL(csd[2], 20, 18) /* [ 52: 50][2,20:18] */
#define SDIO_CSD_C_SIZE_MULT(csd)              SDIO_MASK_VAL(csd[2], 17, 15) /* [ 49: 47][2,17:15] */
#define SDIO_CSD_V1_ERASE_BLK_EN(csd)          SDIO_MASK_VAL(csd[2], 14, 14) /* [ 46: 46][2,14:14] */
#define SDIO_CSD_V1_SECTOR_SIZE(csd)           SDIO_MASK_VAL(csd[2], 13,  7) /* [ 45: 39][2,13: 7] */
#define SDIO_CSD_V1_WP_GRP_SIZE(csd)           SDIO_MASK_VAL(csd[2],  6,  0) /* [ 38: 32][2, 6: 0] */
#define SDIO_CSD_V1_WP_GRP_ENABLE(csd)         SDIO_MASK_VAL(csd[3], 31, 31) /* [ 31: 31][3,31:31] */
#define SDIO_CSD_V1_reserved3(csd)             SDIO_MASK_VAL(csd[3], 30, 29) /* [ 30: 29][3,30:29] */
#define SDIO_CSD_V1_R2W_FACTOR(csd)            SDIO_MASK_VAL(csd[3], 28, 26) /* [ 28: 26][3,28:26] */
#define SDIO_CSD_V1_WRITE_BL_LEN(csd)          SDIO_MASK_VAL(csd[3], 25, 22) /* [ 25: 22][3,25:22] */
#define SDIO_CSD_V1_WRITE_BL_PARTIAL(csd)      SDIO_MASK_VAL(csd[3], 21, 21) /* [ 21: 21][3,21:21] */
#define SDIO_CSD_V1_reserved4(csd)             SDIO_MASK_VAL(csd[3], 20, 16) /* [ 20: 16][3,20:16] */
#define SDIO_CSD_V1_FILE_FORMAT_GRP(csd)       SDIO_MASK_VAL(csd[3], 15, 15) /* [ 15: 15][3,15:15] */
#define SDIO_CSD_V1_COPY(csd)                  SDIO_MASK_VAL(csd[3], 14, 14) /* [ 14: 14][3,14:14] */
#define SDIO_CSD_V1_PERM_WRITE_PROTECT(csd)    SDIO_MASK_VAL(csd[3], 13, 13) /* [ 13: 13][3,13:13] */
#define SDIO_CSD_V1_TMP_WRITE_PROTECT(csd)     SDIO_MASK_VAL(csd[3], 12, 12) /* [ 12: 12][3,12:12] */
#define SDIO_CSD_V1_FILE_FORMAT(csd)           SDIO_MASK_VAL(csd[3], 11, 10) /* [ 11: 10][3,11:10] */
#define SDIO_CSD_V1_reserved5(csd)             SDIO_MASK_VAL(csd[3],  9,  8) /* [  9:  8][3, 9: 8] */
#define SDIO_CSD_V1_CRC(csd)                   SDIO_MASK_VAL(csd[3],  7,  1) /* [  7:  1][3, 7: 1] */
#define SDIO_CSD_V1_reserved6(csd)             SDIO_MASK_VAL(csd[3],  0,  0) /* [  0:  0][3, 0: 0] 固定为1 */

#define SDIO_CSD_V2_STRUCT(csd)                SDIO_MASK_VAL(csd[0], 31, 30) /* [127:126][0,31:30] */
#define SDIO_CSD_V2_reserved0(csd)             SDIO_MASK_VAL(csd[0], 29, 24) /* [125:120][0,29:24] */
#define SDIO_CSD_V2_TAAC(csd)                  SDIO_MASK_VAL(csd[0], 23, 16) /* [119:112][0,23:16] */
#define SDIO_CSD_V2_NSAC(csd)                  SDIO_MASK_VAL(csd[0], 15,  8) /* [111:104][0,15: 8] */
#define SDIO_CSD_V2_TRAN_SPEED_MULT(csd)       SDIO_MASK_VAL(csd[0],  7,  3) /* [103: 99][0, 7: 3] */
#define SDIO_CSD_V2_TRAN_SPEED_NUIT(csd)       SDIO_MASK_VAL(csd[0],  2,  0) /* [ 98: 96][0, 2: 0] */
#define SDIO_CSD_V2_CCC(csd)                   SDIO_MASK_VAL(csd[1], 31, 20) /* [ 95: 84][1,31:20] */
#define SDIO_CSD_V2_READ_BL_LEN(csd)           SDIO_MASK_VAL(csd[1], 19, 16) /* [ 83: 80][1,19:16] */
#define SDIO_CSD_V2_READ_BL_PARTIAL(csd)       SDIO_MASK_VAL(csd[1], 15, 15) /* [ 79: 79][1,15:15] */
#define SDIO_CSD_V2_WRITE_BLK_MISALIGN(csd)    SDIO_MASK_VAL(csd[1], 14, 14) /* [ 78: 78][1,14:14] */
#define SDIO_CSD_V2_READ_BLK_MISALIGN(csd)     SDIO_MASK_VAL(csd[1], 13, 13) /* [ 77: 77][1,13:13] */
#define SDIO_CSD_V2_DSR_IMP(csd)               SDIO_MASK_VAL(csd[1], 12, 12) /* [ 76: 76][1,12:12] */
#define SDIO_CSD_V2_reserved1(csd)             SDIO_MASK_VAL(csd[1], 11,  6) /* [ 75: 70][1,11: 6] */
#define SDIO_CSD_V2_C_SIZE(csd)                ((SDIO_MASK_VAL(csd[1],  5,  0)<<16)|SDIO_MASK_VAL(csd[2], 31, 16)) /* [ 69: 48][1, 5: 0][2,31:16] */
#define SDIO_CSD_V2_reserved2(csd)             SDIO_MASK_VAL(csd[2], 15, 15) /* [ 47: 47][2,15:15] */
#define SDIO_CSD_V2_ERASE_BLK_EN(csd)          SDIO_MASK_VAL(csd[2], 14, 14) /* [ 46: 46][2,14:14] */
#define SDIO_CSD_V2_SECTOR_SIZE(csd)           SDIO_MASK_VAL(csd[2], 13,  7) /* [ 45: 39][2,13: 7] */
#define SDIO_CSD_V2_WP_GRP_SIZE(csd)           SDIO_MASK_VAL(csd[2],  6,  0) /* [ 38: 32][2, 6: 0] */
#define SDIO_CSD_V2_WP_GRP_ENABLE(csd)         SDIO_MASK_VAL(csd[3], 31, 31) /* [ 31: 31][3,31:31] */
#define SDIO_CSD_V2_reserved3(csd)             SDIO_MASK_VAL(csd[3], 30, 29) /* [ 30: 29][3,30:29] */
#define SDIO_CSD_V2_R2W_FACTOR(csd)            SDIO_MASK_VAL(csd[3], 28, 26) /* [ 28: 26][3,28:26] */
#define SDIO_CSD_V2_WRITE_BL_LEN(csd)          SDIO_MASK_VAL(csd[3], 25, 22) /* [ 25: 22][3,25:22] */
#define SDIO_CSD_V2_WRITE_BL_PARTIAL(csd)      SDIO_MASK_VAL(csd[3], 21, 21) /* [ 21: 21][3,21:21] */
#define SDIO_CSD_V2_reserved4(csd)             SDIO_MASK_VAL(csd[3], 20, 16) /* [ 20: 16][3,20:16] */
#define SDIO_CSD_V2_FILE_FORMAT_GRP(csd)       SDIO_MASK_VAL(csd[3], 15, 15) /* [ 15: 15][3,15:15] */
#define SDIO_CSD_V2_COPY(csd)                  SDIO_MASK_VAL(csd[3], 14, 14) /* [ 14: 14][3,14:14] */
#define SDIO_CSD_V2_PERM_WRITE_PROTECT(csd)    SDIO_MASK_VAL(csd[3], 13, 13) /* [ 13: 13][3,13:13] */
#define SDIO_CSD_V2_TMP_WRITE_PROTECT(csd)     SDIO_MASK_VAL(csd[3], 12, 12) /* [ 12: 12][3,12:12] */
#define SDIO_CSD_V2_FILE_FORMAT(csd)           SDIO_MASK_VAL(csd[3], 11, 10) /* [ 11: 10][3,11:10] */
#define SDIO_CSD_V2_reserved5(csd)             SDIO_MASK_VAL(csd[3],  9,  8) /* [  9:  8][3, 9: 8] */
#define SDIO_CSD_V2_CRC(csd)                   SDIO_MASK_VAL(csd[3],  7,  1) /* [  7:  1][3, 7: 1] */
#define SDIO_CSD_V2_reserved6(csd)             SDIO_MASK_VAL(csd[3],  0,  0) /* [  0:  0][3, 0: 0] 固定为1 */

/**
 * 驱动阶段寄存器(DSR, Driver Stage Reigster)，16bits，可用于扩展操作条件中，
 * 用于提高总线性能，CSD中有DSR是否使用的标志。DSR的默认值为0x404
 */

/**
 * SD配置寄存器(SCR, SD Configuration Register)，64bits，提供了SD卡的特殊
 * 功能信息，由卡的生产厂商写入，主机只读。
 */
#define SDIO_SCR_STRUCT_V1                  0x0       /* SCR结构体版本 */
#define SDIO_SCR_SPEC_V1_0                  0x0       /* SD卡物理层规范版本 */
#define SDIO_SCR_SPEC_V1_1                  0x1
#define SDIO_SCR_SPEC_V2_0                  0x2
#define SDIO_SCR_DATA_STAT_AFTER_ERASE_0    0x0       /* 擦除后数据为0 */
#define SDIO_SCR_DATA_STAT_AFTER_ERASE_1    0x1       /* 擦除后数据为1 */
#define SDIO_SCR_SECURITY_NONE              0x0       /* SD不支持安全 */
#define SDIO_SCR_SECURITY_V1_0_1            0x2       /* SD支持安全V1.0.1，标准容量卡固定为该值 */
#define SDIO_SCR_SECURITY_V2_0_0            0x3       /* SD支持安全V2.0.0，大容量卡固定为该值 */
#define SDIO_SCR_BUS_WIDTHS_1               (0x1U<<0) /* SD必须支持1bit位宽 */
#define SDIO_SCR_BUS_WIDTHS_4               (0x1U<<2) /* SD必须支持4bit位宽 */

/* 数据线是大端格式FIFO是小端格式 */
#define SDIO_SCR_STRUCT(scr)                SDIO_MASK_VAL(scr[0], 31, 28) /* [63:60][0,31:28] */
#define SDIO_SCR_SPEC(scr)                  SDIO_MASK_VAL(scr[0], 27, 24) /* [59:56][0,27:24] */
#define SDIO_SCR_DATA_STAT_AFTER_ERASE(scr) SDIO_MASK_VAL(scr[0], 23, 23) /* [55:55][0,23:23] */
#define SDIO_SCR_SECURITY(scr)              SDIO_MASK_VAL(scr[0], 22, 20) /* [54:52][0,22:20] */
#define SDIO_SCR_BUS_WIDTHS(scr)            SDIO_MASK_VAL(scr[0], 19, 16) /* [51:48][0,19:16] */
#define SDIO_SCR_reserved0(scr)                                           /* [47: 0][1,15: 0][0,31: 0] */

/**
 * SD状态数据块(SSR, SD Status)，使用ACMD13获取，大小为512bit(64B)，可选择
 * 性读取一部分，卡在传输模式下（卡被选中）才能发送ACMD13
 */
#define SDIO_SSR_DAT_BUS_WIDTH_1        (0x0U) /* 1bit总线位宽，默认值 */
#define SDIO_SSR_DAT_BUS_WIDTH_4        (0x2U) /* 4bit总线位宽 */
#define SDIO_SSR_SECURED_MODE_DIS       (0x0U) /* 卡处于非安全模式 */
#define SDIO_SSR_SECURED_MODE_EN        (0x1U) /* 卡处于安全模式 */
#define SDIO_SSR_CARD_TYPE_RW           (0x0U) /* 常规可读写的卡 */
#define SDIO_SSR_CARD_TYPE_OR           (0x1U) /* 只读的ROM卡 */
/* 受保护区域的大小，标准容量卡这里需要乘块大小，高容量卡这里是字节数 */
#define _SDIO_SsrSdscProtectedArea(SIZE_OF_PROTECTED_AREA, BLOCK_LEN)  ((SIZE_OF_PROTECTED_AREA)*(BLOCK_LEN))
#define _SDIO_SsrSdhcProtectedArea(SIZE_OF_PROTECTED_AREA)             (SIZE_OF_PROTECTED_AREA)
#define SDIO_SSR_SPEED_CLASS_0          (0x0)  /* 卡的速度等级 */
#define SDIO_SSR_SPEED_CLASS_2          (0x1)
#define SDIO_SSR_SPEED_CLASS_4          (0x2)
#define SDIO_SSR_SPEED_CLASS_6          (0x3)
#define SDIO_SSR_PERFORMANCE_MOVE_V(val) (val) /* 按1MB/s步长表示的移动性能，最小为1MB/s，0xFF表示无穷大 */
#define SDIO_SSR_PERFORMANCE_MOVE_UNDEF 0x0    /* 表示未定义 */
#define SDIO_SSR_PERFORMANCE_MOVE_INF   0xFF   /* 表示无穷大 */
#define SDIO_SSR_AU_SIZE_V(AU_SIZE)     (1024U*(0x1U<<((AU_SIZE)+3))) /* AU大小 */
#define SDIO_SSR_ERASE_SIZE_V(val)      (val)  /* 一次擦除的AU数，ERASE_TIMEOUT定义了该擦除所需要的时间 */
#define SDIO_SSR_ERASE_SIZE_UNDEF       0x0    /* 表示擦除大小和时间未定义 */
#define SDIO_SSR_ERASE_TIMEOUT_V(val)   (val)  /* 擦除UNIT_OF_ERASE_AU指定的区域时的超时值 */
#define SDIO_SSR_ERASE_OFFSET_V(val)    (val)  /* 为擦除时间增加的固定偏移值 */

#define SDIO_SSR_DAT_BUS_WIDTH(ssr)            SDIO_MASK_VAL(ssr[0], 31, 30) /* [511:510][0,31:30] */
#define SDIO_SSR_SECURED_MODE(ssr)             SDIO_MASK_VAL(ssr[0], 29, 29) /* [509:509][0,29:29] */
#define SDIO_SSR_reserved0(ssr)                SDIO_MASK_VAL(ssr[0], 28, 16) /* [508:496][0,28:16] */
#define SDIO_SSR_CARD_TYPE(ssr)                SDIO_MASK_VAL(ssr[0], 15,  0) /* [495:480][0,15: 0] */
#define SDIO_SSR_SIZE_OF_PROTECTED_AREA(ssr)   (ssr[1])                      /* [479:448][1,31: 0]，左移32位会溢出警告 */
#define SDIO_SSR_SPEED_CLASS(ssr)              SDIO_MASK_VAL(ssr[2], 31, 24) /* [447:440][2,31:24] */
#define SDIO_SSR_PERFORMANCE_MOVE(ssr)         SDIO_MASK_VAL(ssr[2], 23, 16) /* [439:432][2,23:16] */
#define SDIO_SSR_AU_SIZE(ssr)                  SDIO_MASK_VAL(ssr[2], 15, 12) /* [431:428][2,15:12] */
#define SDIO_SSR_reserved1(ssr)                SDIO_MASK_VAL(ssr[2], 11,  8) /* [427:424][2,11: 8] */
#define SDIO_SSR_ERASE_SIZE(ssr)               ((SDIO_MASK_VAL(ssr[2],  7,  0)<<8)|SDIO_MASK_VAL(ssr[2], 31, 24)) /* [423:408][2, 7: 0][3,31:24] */
#define SDIO_SSR_ERASE_TIMEOUT(ssr)            SDIO_MASK_VAL(ssr[3], 23, 18) /* [407:402][3,23:18] */
#define SDIO_SSR_ERASE_OFFSET(ssr)             SDIO_MASK_VAL(ssr[3], 17, 16) /* [401:400][3,17:16] */
#define SDIO_SSR_reserved2(ssr)                                              /* [399:  0] */

/**
 * 相对地址(RCA, Relative Card Address)，16bits，可动态变化，初始化时，主机
 * 发送CMD3，卡返回一个相对地址，若主机需要更换则可以重新发CMD3命令，卡会返回
 * 一个新的地址
 */
#define SDIO_RCA

/**
 * 构造一个掩码，比如[5:2]的掩码SDIO_MASK(5, 2)=0x0000003C
 */
#define SDIO_MASK(end, start)   (((0x1U<<((end)-(start)+1))-1)<<(start))
#define SDIO_MASK_VAL(val, end, start) (((val) & SDIO_MASK(end, start))>>(start))
#define SDIO_MASK_SET(val, end, start) ((((uint32_t)(val))<<(start))&SDIO_MASK(end, start))

typedef enum {
	CARD_BUS_WIDE_1 = 0,
	CARD_BUS_WIDE_4 = 2,
} CardBusWide_e;

typedef enum {
	CARD_TYPE_SDSC      = 0,
	CARD_TYPE_SDHC_SDXC = 1,
	CARD_TYPE_SECURED   = 3,
} CardType_e;

typedef struct {
	uint32_t DCTRL;
	CardType_e Type;
	uint16_t BlockLen;
	uint32_t Blocks;
	uint16_t RelCardAddr;
} SdInfo_t;

SdInfo_t sdInfo;

static void mSdioCmd_CMD0_GO_IDLE_STATE(void);
static void mSdioCmd_CMD8_HS_SEND_EXT_CSD(void);
static void mSdioCmd_CMD55_APP_CMD(void);
static void mSdioCmd_ACMD41_SD_APP_OP_COND(void);
static void mSdioCmd_CMD2_ALL_SEND_CID(void);
static void mSdioCmd_CMD3_SET_REL_ADDR(void);
static void mSdioCmd_CMD9_SEND_CSD(void);
static void mSdioCmd_CMD7_SEL_DESEL_CARD(void);
static void mSdioCmd_CMD16_SET_BLOCKLEN(uint32_t len);
static void mSdioCmd_SDIO_ACMD51_SD_APP_SEND_SCR(void);
static void mSdioCmd_ACMD6_APP_SD_SET_BUSWIDTH(CardBusWide_e wide);
static void mSdioCmd_SDIO_ACMD13_SD_APP_STATUS(void);
static void mSdioCmd_CMD17_READ_SINGLE_BLOCK(void *buf, uint32_t addr);
static void mSdioCmd_CMD18_READ_MULT_BLOCK(void *buf, uint32_t addr, uint32_t blocks);
static void mSdioCmd_CMD12_STOP_TRANSMISSION(void);
static void mSdioCmd_CMD32_SD_ERASE_GRP_START(uint32_t addr);
static void mSdioCmd_CMD33_SD_ERASE_GRP_END(uint32_t addr);
static void mSdioCmd_CMD38_ERASE(void);
static uint32_t mSdioCmd_CMD13_SEND_STATUS(void);
static void mSdioCmd_CMD24_WRITE_SINGLE_BLOCK(const void *buf, uint32_t addr);
static void mSdioCmd_CMD25_WRITE_MULT_BLOCK(const void *buf, uint32_t addr, uint32_t blocks);


static void mSdioClockInit(void)
{
	/* 使能SDIO时钟 */
	RCC_EnableApb2(RCC_CLK_APB2_SDIO);
	RCC_EnableAhb1(RCC_CLK_AHB1_GPIOC | RCC_CLK_AHB1_GPIOD);
}

static void mSdioGpioInit(void)
{
	uint32_t tmp32;

	/* 复用模式 */
	tmp32 = SOC_GPIOC.MODER;
	tmp32 &= ~GPIO_MODER_MASK(8);
	tmp32 &= ~GPIO_MODER_MASK(9);
	tmp32 &= ~GPIO_MODER_MASK(10);
	tmp32 &= ~GPIO_MODER_MASK(11);
	tmp32 &= ~GPIO_MODER_MASK(12);
	tmp32 |= GPIO_MODER_AF(8);
	tmp32 |= GPIO_MODER_AF(9);
	tmp32 |= GPIO_MODER_AF(10);
	tmp32 |= GPIO_MODER_AF(11);
	tmp32 |= GPIO_MODER_AF(12);
	SOC_GPIOC.MODER = tmp32;

	/* 推挽输出 */
	tmp32 = SOC_GPIOC.OTYPER;
	tmp32 &= ~GPIO_OTYPER_MASK(8);
	tmp32 &= ~GPIO_OTYPER_MASK(9);
	tmp32 &= ~GPIO_OTYPER_MASK(10);
	tmp32 &= ~GPIO_OTYPER_MASK(11);
	tmp32 &= ~GPIO_OTYPER_MASK(12);
	tmp32 |= GPIO_OTYPER_PP(8);
	tmp32 |= GPIO_OTYPER_PP(9);
	tmp32 |= GPIO_OTYPER_PP(10);
	tmp32 |= GPIO_OTYPER_PP(11);
	tmp32 |= GPIO_OTYPER_PP(12);
	SOC_GPIOC.OTYPER = tmp32;

	/* 50M的输出速度 */
	tmp32 = SOC_GPIOC.OSPEEDR;
	tmp32 &= ~GPIO_OSPEEDR_MASK(8);
	tmp32 &= ~GPIO_OSPEEDR_MASK(9);
	tmp32 &= ~GPIO_OSPEEDR_MASK(10);
	tmp32 &= ~GPIO_OSPEEDR_MASK(11);
	tmp32 &= ~GPIO_OSPEEDR_MASK(12);
	tmp32 |= GPIO_OSPEEDR_50M(8);
	tmp32 |= GPIO_OSPEEDR_50M(9);
	tmp32 |= GPIO_OSPEEDR_50M(10);
	tmp32 |= GPIO_OSPEEDR_50M(11);
	tmp32 |= GPIO_OSPEEDR_50M(12);
	SOC_GPIOC.OSPEEDR = tmp32;

	/* 禁用上下拉 */
	tmp32 = SOC_GPIOC.PUPDR;
	tmp32 &= ~GPIO_PUPDR_MASK(8);
	tmp32 &= ~GPIO_PUPDR_MASK(9);
	tmp32 &= ~GPIO_PUPDR_MASK(10);
	tmp32 &= ~GPIO_PUPDR_MASK(11);
	tmp32 &= ~GPIO_PUPDR_MASK(12);
	tmp32 |= GPIO_PUPDR_NONE(8);
	tmp32 |= GPIO_PUPDR_NONE(9);
	tmp32 |= GPIO_PUPDR_NONE(10);
	tmp32 |= GPIO_PUPDR_NONE(11);
	tmp32 |= GPIO_PUPDR_NONE(12);
	SOC_GPIOC.PUPDR = tmp32;

	/* IO复用为SDIO */
	tmp32 = SOC_GPIOC.AFHR;
	tmp32 &= ~GPIO_AFHR_MASK(8);
	tmp32 &= ~GPIO_AFHR_MASK(9);
	tmp32 &= ~GPIO_AFHR_MASK(10);
	tmp32 &= ~GPIO_AFHR_MASK(11);
	tmp32 &= ~GPIO_AFHR_MASK(12);
	tmp32 |= GPIO_AFHR_0_15D(8,  12);
	tmp32 |= GPIO_AFHR_0_15D(9,  12);
	tmp32 |= GPIO_AFHR_0_15D(10, 12);
	tmp32 |= GPIO_AFHR_0_15D(11, 12);
	tmp32 |= GPIO_AFHR_0_15D(12, 12);
	SOC_GPIOC.AFHR = tmp32;

	/* 复用模式 */
	tmp32 = SOC_GPIOD.MODER;
	tmp32 &= ~GPIO_MODER_MASK(2);
	tmp32 |= GPIO_MODER_AF(2);
	SOC_GPIOD.MODER = tmp32;

	/* 推挽输出 */
	tmp32 = SOC_GPIOD.OTYPER;
	tmp32 &= ~GPIO_OTYPER_MASK(2);
	tmp32 |= GPIO_OTYPER_PP(2);
	SOC_GPIOD.OTYPER = tmp32;

	/* 50M的输出速度 */
	tmp32 = SOC_GPIOD.OSPEEDR;
	tmp32 &= ~GPIO_OSPEEDR_MASK(2);
	tmp32 |= GPIO_OSPEEDR_50M(2);
	SOC_GPIOD.OSPEEDR = tmp32;

	/* 禁用上下拉 */
	tmp32 = SOC_GPIOD.PUPDR;
	tmp32 &= ~GPIO_PUPDR_MASK(2);
	tmp32 |= GPIO_PUPDR_NONE(2);
	SOC_GPIOD.PUPDR = tmp32;

	/* IO复用为SDIO */
	tmp32 = SOC_GPIOD.AFLR;
	tmp32 &= ~GPIO_AFLR_MASK(2);
	tmp32 |= GPIO_AFLR_0_15D(2, 12);
	SOC_GPIOD.AFLR = tmp32;
}

static void mSdioInterrupt(void)
{
	/* 允许错误中断[9:9] [5:0] */
	//SOC_SDIO.MASK = SDIO_FLAG_ERRORBIT;
	NVIC_SetPriority(NVIC_IRQ_SDIO, NVIC_PRIORITY_LOW,
	                 NVIC_PRIORITY_SUB_NONE);
	NVIC_EnableIrq(NVIC_IRQ_SDIO);
}

void mSdioInit(void)
{
	mSdioClockInit();
	mSdioGpioInit();
	mSdioInterrupt();
}

/**
 * Idle命令，复位SD卡
 */
static void mSdioCmd_CMD0_GO_IDLE_STATE(void)
{
	/* 无参数 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(0x0);
	/* 广播命令(bc) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_GO_IDLE_STATE) |
	               SDIO_CMD_WAITRESP_NONE | // 无响应
	               SDIO_CMD_CPSMEN_EN;      // 使能命令状态机
	/* 等待命令发送完成 */
	while (!(SOC_SDIO.STA & SDIO_FLAG_CMDSENT));
	/* 清除状态标志 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDSENT;
}

/* *
 * 该命令仅SD V2.x版本支持，在CMD0_GO_IDLE_STATE之后发送，卡检测主机提供支持
 * 的电压是否支持，同时启用已存在命令/响应的扩展功能。如果不支持则卡不予响应。
 * 对于V1.x的卡也不会响应该命令。如卡未响应，则主机应再发送ACMD41。
 */
static void mSdioCmd_CMD8_HS_SEND_EXT_CSD(void)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	     [39:20][31:12]20: 保留: 0
	     [19:16][11: 8] 4: 支持电压, 1:2.7~3.6V 2:低压卡 3:两个都支持
	     [15: 8][ 7: 0] 8: 魔数，卡会返回该值用于通信校验，推荐0xAA */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(
	               SDIO_MASK_SET(0x0, 31, 12) |
	               SDIO_MASK_SET(0x1, 11,  8) | // 主机仅支持2.7~3.6V
	               SDIO_MASK_SET(0xAA, 7,  0));
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_HS_SEND_EXT_CSD) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		/* 命令响应超时，表示电压不支持或SD卡是1.x版本 */
		if (sta & SDIO_FLAG_CTIMEOUT) {
			MLOG_DEBUG(1,
				mDebugPutStr("电压不支持或SD卡是1.x版本或不是SD卡，请使用ACMD41");
			);
		}
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R7，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_HS_SEND_EXT_CSD) {
		while (1);
	}
	/* R7：
	     [39:20][31:12]20: 保留: 0
	     [19:16][11: 8] 4: 支持电压, 1:2.7~3.6V 2:低压卡 3:两个都支持
	     [15: 8][ 7: 0] 8: 魔数，卡会返回该值用于通信校验，推荐0xAA */
	res = SOC_SDIO.RESP1;
	/* 判断魔数回传的是否正确 */
	if (SDIO_MASK_VAL(res, 7, 0) != 0xAA) {
		while (1);
	}
	MLOG_DEBUG(1,
		mDebugPutStr("[SD版本]：V2.x   [电压]：");
		uint32_t tmp1 = SDIO_MASK_VAL(res, 11, 8);
		if (tmp1 == 0x1) {
			mDebugPutStr("2.7~3.6V");
		} else if (tmp1 == 0x2) {
			mDebugPutStr("低电压");
		} else if (tmp1 == 0x3) {
			mDebugPutStr("双电压");
		} else {
			mDebugPrintf("未知(%d)", tmp1);
		}
	);
}

/**
 * 发送非标准命令即应用专属命令，需要先发送CMD55命令
 */
static void mSdioCmd_CMD55_APP_CMD(void)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	     [31:16]: RCA，卡的相对地址，初始化时卡的相对地址为0
	     [15: 0]: 无用 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(((uint32_t)sdInfo.RelCardAddr)<<16);
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_APP_CMD) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机

	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		if (sta & SDIO_FLAG_CTIMEOUT) {
			/* RelCardAddr为0表示正在进行卡识别 */
			MLOG_DEBUG(sdInfo.RelCardAddr == 0,
				mDebugPutStr("可能是MMC卡，使用CMD1初始化");
			);
		}
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_APP_CMD) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
}

/* *
 * 询问卡的操作条件，发送该命令前应先发送CMD8判断卡是否是V2.x版本的。该命令
 * 发送主机支持的电压以及容量版本，卡会进行校验，同时反馈卡是否已就绪，以及卡
 * 是标准容量还是大容量，大容量的卡不支持标准容量的主机，但是标准容量的卡支持
 * 大容量的主机。
 *
 */
static void mSdioCmd_ACMD41_SD_APP_OP_COND(void)
{
	uint32_t sta;
	uint32_t res;

	/* 轮询直到卡就绪，最长不超过1s */
	do {
		/* 每次都需要先发CMD55命令 */
		mSdioCmd_CMD55_APP_CMD();

		/* 参数是OCR的格式一致，参数为0时，可用于查询卡的容量以及电压范围，
		   不会改变卡的状态，电压范围可以是多个或起来在卡的支持范围内都可以
		   卡默认都支持2.7~3.6V */
		SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(
		               SDIO_OCR_CCS_HIGH |  // 主机支持高容量卡，兼容标准容量卡
		               SDIO_OCR_3_2V_3_3V); // 主机的电压是3.2~3.3V
		/* 带响应的广播命令(bcr) */
		SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SD_APP_OP_COND) |
		               SDIO_CMD_WAITRESP_SHORT | // 短响应
		               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机

		/* 等待命令完成 */
		do {
			sta = SOC_SDIO.STA;
		} while (sta & SDIO_FLAG_CMDACT);

		/* 响应状态中应当只有收到响应且CRC验证失败，R3响应中不包含CRC */
		if (sta & (~SDIO_FLAG_CCRCFAIL)) {
			while (1);
		}
		/* 清除状态 */
		SOC_SDIO.ICR = SDIO_FLAG_CCRCFAIL;

		/* 响应格式为R3，校验命令索引，R3命令索引保留为0x3F */
		if (SOC_SDIO.RESPCMD != 0x3F) {
			while (1);
		}
		/* 响应的内容是操作条件寄存器(OCR, Operation Conditions Register) */
		res = SOC_SDIO.RESP1;
	} while (!(res & SDIO_OCR_READY_EN));

	/* 判断卡的类型 */
	if (res & SDIO_OCR_CCS_HIGH) {
		/* SDHC(32GB) SDXC(2TB) */
		sdInfo.Type = CARD_TYPE_SDHC_SDXC;
	} else {
		/* SDSC(2GB) */
		sdInfo.Type = CARD_TYPE_SDSC;
	}
	MLOG_DEBUG(1,
		mDebugPutStr("[卡类型]： ");
		if (res & SDIO_OCR_CCS_HIGH) {
			mDebugPutStr("SDHC/XC 大于2GB");
		} else {
			mDebugPutStr("SDSC 小于2GB");
		}
	);
}

/* *
 * 查询卡的CID(Card Identification)信息
 */
static void mSdioCmd_CMD2_ALL_SEND_CID(void)
{
	uint32_t sta;
	uint32_t cid[4];

	/* 无参数 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(0x0);
	/* 带响应的广播命令(bcr) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_ALL_SEND_CID) |
	               SDIO_CMD_WAITRESP_LONG |  // 长响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R2，校验命令索引，R2命令索引保留为0x3F */
	if (SOC_SDIO.RESPCMD != 0x3F) {
		while (1);
	}
	/* 响应的内容是卡识别寄存器(CID, Card Identification Number) */
	cid[0] = SOC_SDIO.RESP1;
	cid[1] = SOC_SDIO.RESP2;
	cid[2] = SOC_SDIO.RESP3;
	cid[3] = SOC_SDIO.RESP4;

	MLOG_DEBUG_NO_ENDL(1,
		mDebugPutMemoryForU32("卡识别寄存器：", cid, 4);
		mDebugPrintf(MLOG_SPACE "[制造商]：%02x" MLOG_ENDL, SDIO_CID_MID(cid));
		mDebugPrintf(MLOG_SPACE "[应用ID]：%c%c" MLOG_ENDL,
		             SDIO_CID_OID0(cid), SDIO_CID_OID1(cid));
		mDebugPrintf(MLOG_SPACE "[产品名称]：%c%c%c%c%c" MLOG_ENDL,
		             SDIO_CID_PNM0(cid), SDIO_CID_PNM1(cid),
		             SDIO_CID_PNM2(cid), SDIO_CID_PNM3(cid),
		             SDIO_CID_PNM4(cid));
		mDebugPrintf(MLOG_SPACE "[产品版本]：%d.%d" MLOG_ENDL,
		             SDIO_CID_PRVH(cid), SDIO_CID_PRVL(cid));
		mDebugPrintf(MLOG_SPACE "[序列号]：%08x" MLOG_ENDL, SDIO_CID_PSN(cid));
		mDebugPrintf(MLOG_SPACE "[生产日期]：%d-%d" MLOG_ENDL,
		             SDIO_CID_MDTY(cid)+2000, SDIO_CID_MDTM(cid));
	);
}

/* *
 * 请求卡设置一个相对地址，相对地址由卡建议，主机核准，如果主机不同意该地址，
 * 可重新发送该命令，卡会更新一个地址。
 */
static void mSdioCmd_CMD3_SET_REL_ADDR(void)
{
	uint32_t sta;
	uint32_t res;

	/* 无参数 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(0x0);
	/* 带响应的广播命令(bcr) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SET_REL_ADDR) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R6，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SET_REL_ADDR) {
		while (1);
	}
	/* 响应的内容:
	       [39:24][31:16]: RCA，相对地址
	       [23:22][15:14]: CSR[23:22],卡状态寄存器
	       [21:21][13:13]: CSR[19:19]
	       [20: 8][12: 0]: CSR[12: 0] */
	res = SOC_SDIO.RESP1;
	if (!(res & SDIO_R6_CSR_ERRORBITS)) {
		/* 返回值中无错误状态，则记录卡的相对地址 */
		sdInfo.RelCardAddr = SDIO_MASK_VAL(res, 31, 16);
	}
	MLOG_DEBUG(1,
		mDebugPrintf("[卡相对地址]：%04x", sdInfo.RelCardAddr);
	);
}

/* *
 * 查询卡的CSD(Card Specific Data)信息
 */
static void mSdioCmd_CMD9_SEND_CSD(void)
{
	uint32_t sta;
	uint32_t csd[4];

	/* 参数：
	     [31:16]: RCA，卡的相对地址
	     [15: 0]: 无用 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(((uint32_t)sdInfo.RelCardAddr)<<16);
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SEND_CSD) |
	               SDIO_CMD_WAITRESP_LONG |  // 长响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R2，校验命令索引，R2中命令索引全为1 */
	if (SOC_SDIO.RESPCMD != 0x3F) {
		while (1);
	}
	/* 响应的内容是CSD(Card Specific Data),128bits */
	csd[0] = SOC_SDIO.RESP1;
	csd[1] = SOC_SDIO.RESP2;
	csd[2] = SOC_SDIO.RESP3;
	csd[3] = SOC_SDIO.RESP4;

	if (SDIO_CSD_STRUCT(csd) == SDIO_CSD_STRUCT_SDSC) {
		sdInfo.BlockLen = _SDIO_CsdV1BlockLen(SDIO_CSD_V1_READ_BL_LEN(csd));
		sdInfo.Blocks = _SDIO_CsdV1BlockCount(SDIO_CSD_V1_C_SIZE(csd),
		                                      SDIO_CSD_C_SIZE_MULT(csd));
	} else {
		sdInfo.BlockLen = 512;
		sdInfo.Blocks = _SDIO_CsdV2BlockCount(SDIO_CSD_V2_C_SIZE(csd));
	}

	uint32_t tmp1 = sdInfo.BlockLen;
	uint32_t tmp2 = 0;
	while (!(tmp1 & 0x1)) {
		tmp2++;
		tmp1 >>= 1;
	}
	/* 计算DCTRL_DBLOCKSIZE字段的值 */
	sdInfo.DCTRL = SDIO_DCTRL_DBLOCKSIZE(tmp2);

	MLOG_DEBUG_NO_ENDL(1,
		uint64_t tmp_u64;
		float mult;
		uint32_t unit_val;
		const char *unit_str;
		uint32_t tmp1;
		uint32_t tmp2;
		mDebugPutMemoryForU32("卡特殊信息(CSD)：", csd, 4);
		mDebugPutStr(MLOG_SPACE "[CSD版本]：");
		if (SDIO_CSD_STRUCT(csd) == SDIO_CSD_STRUCT_SDSC) {
			mDebugPuts("V1.0");
			switch (SDIO_CSD_V1_VDD_R_CURR_MIN(csd)) {
				case SDIO_CSD_V1_VDD_x_CURR_MIN_0_5MA:
					unit_str = "0.5";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_1MA:
					unit_str = "1";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_5MA:
					unit_str = "5";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_10MA:
					unit_str = "10";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_25MA:
					unit_str = "25";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_35MA:
					unit_str = "35";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_60MA:
					unit_str = "60";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_100MA:
					unit_str = "100";
					break;
			}
			mDebugPrintf(MLOG_SPACE "[读数据电流]：%smA-", unit_str);
			switch (SDIO_CSD_V1_VDD_R_CURR_MAX(csd)) {
				case SDIO_CSD_V1_VDD_x_CURR_MAX_1MA:
					unit_str = "1";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_5MA:
					unit_str = "5";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_10MA:
					unit_str = "10";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_25MA:
					unit_str = "25";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_35MA:
					unit_str = "35";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_45MA:
					unit_str = "45";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_80MA:
					unit_str = "80";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_200MA:
					unit_str = "200";
					break;
			}
			mDebugPutStr(unit_str);
			mDebugPuts("mA");
			switch (SDIO_CSD_V1_VDD_W_CURR_MIN(csd)) {
				case SDIO_CSD_V1_VDD_x_CURR_MIN_0_5MA:
					unit_str = "0.5";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_1MA:
					unit_str = "1";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_5MA:
					unit_str = "5";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_10MA:
					unit_str = "10";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_25MA:
					unit_str = "25";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_35MA:
					unit_str = "35";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_60MA:
					unit_str = "60";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MIN_100MA:
					unit_str = "100";
					break;
			}
			mDebugPrintf(MLOG_SPACE "[写数据电流]：%smA-", unit_str);
			switch (SDIO_CSD_V1_VDD_W_CURR_MAX(csd)) {
				case SDIO_CSD_V1_VDD_x_CURR_MAX_1MA:
					unit_str = "1";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_5MA:
					unit_str = "5";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_10MA:
					unit_str = "10";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_25MA:
					unit_str = "25";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_35MA:
					unit_str = "35";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_45MA:
					unit_str = "45";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_80MA:
					unit_str = "80";
					break;
				case SDIO_CSD_V1_VDD_x_CURR_MAX_200MA:
					unit_str = "200";
					break;
			}
			mDebugPutStr(unit_str);
			mDebugPuts("mA");
			switch (SDIO_CSD_V1_TAAC_MULT(csd)) {
				case SDIO_CSD_V1_TAAC_MULT_1_0:
					mult = 1.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_1_2:
					mult = 1.2f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_1_3:
					mult = 1.3f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_1_5:
					mult = 1.5f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_2_0:
					mult = 2.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_2_5:
					mult = 2.5f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_3_0:
					mult = 3.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_3_5:
					mult = 3.5f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_4_0:
					mult = 4.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_4_5:
					mult = 4.5f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_5_0:
					mult = 5.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_5_5:
					mult = 5.5f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_6_0:
					mult = 6.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_7_0:
					mult = 7.0f;
					break;
				case SDIO_CSD_V1_TAAC_MULT_8_0:
					mult = 8.0f;
					break;
				default:
					while (1);
			}
			switch (SDIO_CSD_V1_TAAC_UNIT(csd)) {
				case SDIO_CSD_V1_TAAC_UNIT_1NS:
					unit_str = "ns";
					unit_val = 1;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_10NS:
					unit_str = "ns";
					unit_val = 10;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_100NS:
					unit_str = "ns";
					unit_val = 100;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_1US:
					unit_str = "us";
					unit_val = 1;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_10US:
					unit_str = "us";
					unit_val = 10;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_100US:
					unit_str = "us";
					unit_val = 100;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_1MS:
					unit_str = "ms";
					unit_val = 1;
					break;
				case SDIO_CSD_V1_TAAC_UNIT_10MS:
					unit_str = "ms";
					unit_val = 10;
					break;
				default:
					while (1);
			}
			mult *= unit_val;
			tmp1 = SDIO_CSD_V1_NSAC_CYCLES(SDIO_CSD_V1_NSAC(csd));
			mDebugPrintf(MLOG_SPACE "[读时间]：%f%s + %dcycles" MLOG_ENDL,
			             mult, unit_str, tmp1);
			tmp2 = SDIO_CSD_V1_R2W_FACTOR_V(SDIO_CSD_V1_R2W_FACTOR(csd));
			mDebugPrintf(MLOG_SPACE "[写时间]：%f%s + %dcycles" MLOG_ENDL,
			             mult*tmp2, unit_str, tmp1*tmp2);
			tmp1 = _SDIO_CsdV1BlockLen(SDIO_CSD_V1_READ_BL_LEN(csd));
			tmp2 = _SDIO_CsdV1BlockCount(SDIO_CSD_V1_C_SIZE(csd),
			                             SDIO_CSD_C_SIZE_MULT(csd));
			mDebugPrintf(MLOG_SPACE "[块大小]：%d" MLOG_ENDL, tmp1);
			mDebugPrintf(MLOG_SPACE "[块数量]：%d" MLOG_ENDL, tmp2);
			tmp_u64 = tmp1;
			tmp_u64 *= tmp2;
			mDebugPrintf(MLOG_SPACE "[卡容量]：%llu" MLOG_ENDL, tmp_u64);
			mDebugPrintf(MLOG_SPACE "[扇区中块数量]：%d" MLOG_ENDL,
			             SDIO_CSD_V1_SECTOR_SIZE_V(
			             SDIO_CSD_V1_SECTOR_SIZE(csd)));
		} else {
			mDebugPuts("V2.0");
			mDebugPuts(MLOG_SPACE "[读时间]：100ms");
			mDebugPuts(MLOG_SPACE "[写时间]：250ms");
			tmp1 = _SDIO_CsdV2BlockLen();
			tmp2 = _SDIO_CsdV2BlockCount(SDIO_CSD_V2_C_SIZE(csd));
			mDebugPrintf(MLOG_SPACE "[块大小]：%d" MLOG_ENDL, tmp1);
			mDebugPrintf(MLOG_SPACE "[块数量]：%d" MLOG_ENDL, tmp2);
			tmp_u64 = tmp1;
			tmp_u64 *= tmp2;
			mDebugPrintf(MLOG_SPACE "[卡容量]：%llu" MLOG_ENDL, tmp_u64);
		}
		switch (SDIO_CSD_V1_TRAN_SPEED_MULT(csd)) {
			case SDIO_CSD_V1_TRAN_SPEED_MULT_1_0:
				mult = 1.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_1_2:
				mult = 1.2f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_1_3:
				mult = 1.3f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_1_5:
				mult = 1.5f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_2_0:
				mult = 2.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_2_5:
				mult = 2.5f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_3_0:
				mult = 3.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_3_5:
				mult = 3.5f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_4_0:
				mult = 4.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_4_5:
				mult = 4.5f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_5_0:
				mult = 5.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_5_5:
				mult = 5.5f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_6_0:
				mult = 6.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_7_0:
				mult = 7.0f;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_MULT_8_0:
				mult = 8.0f;
				break;
			default:
				while (1);
		}
		switch (SDIO_CSD_V1_TRAN_SPEED_UNIT(csd)) {
			case SDIO_CSD_V1_TRAN_SPEED_UNIT_100K:
				unit_str = "Kbps";
				unit_val = 100;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_UNIT_1M:
				unit_str = "Mbps";
				unit_val = 1;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_UNIT_10M:
				unit_str = "Mbps";
				unit_val = 10;
				break;
			case SDIO_CSD_V1_TRAN_SPEED_UNIT_100M:
				unit_str = "Mbps";
				unit_val = 100;
				break;
			default:
				while (1);
		}
		mult *= unit_val;
		mDebugPrintf(MLOG_SPACE "[最大速度]：%.1f%s" MLOG_ENDL, mult, unit_str);
		mDebugPutStr(MLOG_SPACE "[命令集]：");
		tmp1 = SDIO_CSD_V1_CCC(csd);
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE0) {
			mDebugPutStr("C0 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE1) {
			mDebugPutStr("C1 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE2) {
			mDebugPutStr("C2 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE3) {
			mDebugPutStr("C3 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE4) {
			mDebugPutStr("C4 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE5) {
			mDebugPutStr("C5 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE6) {
			mDebugPutStr("C6 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE7) {
			mDebugPutStr("C7 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE8) {
			mDebugPutStr("C8 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE9) {
			mDebugPutStr("C9 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE10) {
			mDebugPutStr("C10 ");
		}
		if (tmp1 & SDIO_CSD_V1_CCC_CLASE11) {
			mDebugPutStr("C11 ");
		}
		mDebugLineFeed();
		mDebugPutStr(MLOG_SPACE "[字节读]：");
		if (SDIO_CSD_V1_READ_BL_PARTIAL(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[跨块读]：");
		if (SDIO_CSD_V1_READ_BLK_MISALIGN(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[字节写]：");
		if (SDIO_CSD_V1_WRITE_BL_PARTIAL(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[跨块写]：");
		if (SDIO_CSD_V1_WRITE_BLK_MISALIGN(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[DSR]：");
		if (SDIO_CSD_V1_DSR_IMP(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[块擦除]：");
		if (SDIO_CSD_V1_ERASE_BLK_EN(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[写保护组]：");
		if (SDIO_CSD_V1_WP_GRP_ENABLE(csd)) {
			mDebugPuts("Y");
			mDebugPrintf(MLOG_SPACE "[写保护组扇区数]：%d" MLOG_ENDL,
			         SDIO_CSD_V1_WP_GRP_SIZE_V(SDIO_CSD_V1_WP_GRP_SIZE(csd)));
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[拷贝卡]：");
		if (SDIO_CSD_V1_COPY(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[永久写保护]：");
		if (SDIO_CSD_V1_PERM_WRITE_PROTECT(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		mDebugPutStr(MLOG_SPACE "[临时写保护]：");
		if (SDIO_CSD_V1_TMP_WRITE_PROTECT(csd)) {
			mDebugPuts("Y");
		} else {
			mDebugPuts("N");
		}
		if (SDIO_CSD_V1_FILE_FORMAT_GRP(csd)) {
			mDebugPutStr(MLOG_SPACE "[文件系统]：");
			switch (SDIO_CSD_V1_FILE_FORMAT(csd)) {
				case SDIO_CSD_V1_FILE_FORMAT_HARD_DISK:
					mDebugPuts("类似硬盘，带分区表");
					break;
				case SDIO_CSD_V1_FILE_FORMAT_DOS_FAT:
					mDebugPuts("类似DOS FAT，有启动扇区，不带分区表");
					break;
				case SDIO_CSD_V1_FILE_FORMAT_UNIVERSAL:
					mDebugPuts("通用");
					break;
				case SDIO_CSD_V1_FILE_FORMAT_UNKNOWN:
					mDebugPuts("未知");
					break;
			}
		}
	);
}

/* *
 * 通过卡的相对地址选中卡
 */
static void mSdioCmd_CMD7_SEL_DESEL_CARD(void)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	     [31:16]: RCA，卡的相对地址
	     [15: 0]: 无用 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(((uint32_t)sdInfo.RelCardAddr)<<16);
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SEL_DESEL_CARD) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SEL_DESEL_CARD) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
}

/* *
 * 设置命令操作卡时数据块的大小，读写时通常是512字节，SDSC需要设置，SDHC/SDXC
 * 不需要设置，始终固定为512字节
 */
static void mSdioCmd_CMD16_SET_BLOCKLEN(uint32_t len)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	    [31:0] 后续命令操作的数据块大小 */
	SOC_SDIO.ARG = len;
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SET_BLOCKLEN) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SET_BLOCKLEN) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
}

/* *
 * 读取SCR(SD Configuration Register)寄存器，读取SCR不需要通过
 * CMD16_SET_BLOCKLEN设置读取块的长度
 */
static void mSdioCmd_SDIO_ACMD51_SD_APP_SEND_SCR(void)
{
	uint32_t sta;
	uint32_t res;
	uint32_t scr[2];
	uint32_t i;

	/* 需要先发送CMD55命令 */
	mSdioCmd_CMD55_APP_CMD();

	/* 接收数据超时时间 */
	SOC_SDIO.DTIMER = 0xFFFFFFFF;
	/* 接收数据的字节长度 */
	SOC_SDIO.DLEN = 8;
	/* 接收控制参数，块长度必须为8字节 */
	SOC_SDIO.DCTRL = SDIO_DCTRL_DTEN_EN |       // 使能数据传输
	                 SDIO_DCTRL_DTDIR_IN |      // 数据接收
	                 SDIO_DCTRL_DTMODE_BLOCK |  // 块传输
	                 SDIO_DCTRL_DMAEN_DIS |     // 禁用DMA
	                 SDIO_DCTRL_DBLOCKSIZE_8B;  // 块大小8字节

	/* 无参数 */
	SOC_SDIO.ARG = 0x0;
	/* 编址（点到点）数据传输命令，包含数据传输 */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SD_APP_SEND_SCR) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);
	/* 等待数据接收完成，期间需要将有效数据读出，否者DCOUNT为0时，
	   SDIO_FLAG_RXACT不会清除 */
	i = 0;
	do {
		sta = SOC_SDIO.STA;
		if (sta & SDIO_FLAG_RXDAVL) {
			scr[i++] = SOC_SDIO.FIFO[0];
		}
	} while (sta & SDIO_FLAG_RXACT);

	/* 检查响应状态包含：收到响应且CRC正常、数据结束SCOUNT为0、数据已接收
	   且CRC正常、RX FIFO为空 */
	if (sta & (~(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND |
	             SDIO_FLAG_RXFIFOE))) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SD_APP_SEND_SCR) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
	/* 数据的内容是SD配置寄存器(SCR, SD Configuration Register)，数据是大端
	   发过来的，FIFO是小端的，需要做一次字节序反转 */
	_mDevU32EndianSwap(scr, scr, 2);
	MLOG_DEBUG(1,
		mDebugPutMemoryForU32("配置寄存器(SCR)：", scr, 2);
		mDebugPutStr(MLOG_SPACE "[结构体版本]：");
		if (SDIO_SCR_STRUCT(scr) == SDIO_SCR_STRUCT_V1) {
			mDebugPuts("V1.0");
		} else {
			mDebugPuts("未知");
		}
		mDebugPutStr(MLOG_SPACE "[物理层规范]：");
		switch (SDIO_SCR_SPEC(scr)) {
			case SDIO_SCR_SPEC_V1_0:
				mDebugPuts("V1.0");
				break;
			case SDIO_SCR_SPEC_V1_1:
				mDebugPuts("V1.1");
				break;
			case SDIO_SCR_SPEC_V2_0:
				mDebugPuts("V2.0");
				break;
		}
		mDebugPutStr(MLOG_SPACE "[擦除值]：");
		if (SDIO_SCR_DATA_STAT_AFTER_ERASE(scr) ==
		    SDIO_SCR_DATA_STAT_AFTER_ERASE_0) {
			mDebugPuts("0");
		} else {
			mDebugPuts("1");
		}
		mDebugPutStr(MLOG_SPACE "[安全规范]：");
		switch (SDIO_SCR_SECURITY(scr)) {
			case SDIO_SCR_SECURITY_NONE:
				mDebugPuts("不支持");
				break;
			case SDIO_SCR_SECURITY_V1_0_1:
				mDebugPuts("V1.0.1");
				break;
			case SDIO_SCR_SECURITY_V2_0_0:
				mDebugPuts("V2.0.0");
				break;
		}
		mDebugPutStr(MLOG_SPACE "[数据总线宽度]：");
		if (SDIO_SCR_BUS_WIDTHS(scr) & SDIO_SCR_BUS_WIDTHS_1) {
			mDebugPutStr("1B ");
		}
		if (SDIO_SCR_BUS_WIDTHS(scr) & SDIO_SCR_BUS_WIDTHS_4) {
			mDebugPutStr("4B ");
		}
		if (SDIO_SCR_BUS_WIDTHS(scr) &
		    (~(SDIO_SCR_BUS_WIDTHS_1|SDIO_SCR_BUS_WIDTHS_4))) {
			mDebugPutStr("其他");
		}
	);
}

/* *
 * 设置总线位宽，0: 1bit 2: 4bit，该命令需要在卡非锁定的情况下才能使用，否者
 * 会被拒绝
 */
static void mSdioCmd_ACMD6_APP_SD_SET_BUSWIDTH(CardBusWide_e wide)
{
	uint32_t sta;
	uint32_t res;

	/* 需要先发送CMD55命令 */
	mSdioCmd_CMD55_APP_CMD();

	/* 参数：
	     [31: 2]: 保留
	     [ 1: 0]: 数据总线位宽，0:1bit 2:4bit */
	SOC_SDIO.ARG = wide;
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_APP_SD_SET_BUSWIDTH) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_APP_SD_SET_BUSWIDTH) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}

	res = SOC_SDIO.CLKCR & (~SDIO_CLKCR_WIDBUS_MASK);

	if (wide == CARD_BUS_WIDE_1) {
		SOC_SDIO.CLKCR = res | SDIO_CLKCR_WIDBUS_1; // 通信过程使用1bit
		MLOG_DEBUG(1,
			mDebugPutStr("切换到1Bit模式");
		);
	} else {
		SOC_SDIO.CLKCR = res | SDIO_CLKCR_WIDBUS_4; // 通信过程使用1bit
		MLOG_DEBUG(1,
			mDebugPutStr("切换到4Bit模式");
		);
	}
}

/* *
 * 该命令用于读取SD状态数据块(SSR, SD Status)，大小为512bit(64B)，读取SSR
 * 不需要通过CMD16_SET_BLOCKLEN设置读取块的长度
 */
static void mSdioCmd_SDIO_ACMD13_SD_APP_STATUS(void)
{
	uint32_t sta;
	uint32_t res;
	uint32_t ssr[16];
	uint32_t i;

	/* 需要先发送CMD55命令 */
	mSdioCmd_CMD55_APP_CMD();

	/* 接收数据超时时间 */
	SOC_SDIO.DTIMER = 0xFFFFFFFF;
	/* 接收数据的字节长度 */
	SOC_SDIO.DLEN = 64;
	/* 接收控制参数，数据块长度必须是64字节，否者会出现接收数据的CRC错误 */
	SOC_SDIO.DCTRL = SDIO_DCTRL_DTEN_EN |       // 使能数据传输
	                 SDIO_DCTRL_DTDIR_IN |      // 数据接收
	                 SDIO_DCTRL_DTMODE_BLOCK |  // 块传输
	                 SDIO_DCTRL_DMAEN_DIS |     // 禁用DMA
	                 SDIO_DCTRL_DBLOCKSIZE_64B; // 块大小64字节

	/* 无参数 */
	SOC_SDIO.ARG = 0x0;
	/* 编址（点到点）数据传输命令，包含数据传输 */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SD_APP_STATUS) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);
	/* 等待数据接收完成，期间需要将有效数据读出，否者DCOUNT为0时，
	   SDIO_FLAG_RXACT不会清除 */
	i = 0;
	do {
		sta = SOC_SDIO.STA;
		if (sta & SDIO_FLAG_RXDAVL) {
			ssr[i++] = SOC_SDIO.FIFO[0];
		}
	} while (sta & SDIO_FLAG_RXACT);
	/* 检查长度 */
	if (i != 16) {
		while (1);
	}

	/* 检查响应状态包含：收到响应且CRC正常、数据结束SCOUNT为0、数据已接收
	   且CRC正常、RX FIFO为空 */
	if (sta & (~(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND |
	             SDIO_FLAG_RXFIFOE))) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SD_APP_STATUS) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
	/* 数据的内容是SD状态数据块(SSR, SD Status)，数据是大端
	   发过来的，FIFO是小端的，需要做一次字节序反转 */
	_mDevU32EndianSwap(ssr, ssr, 16);
	MLOG_DEBUG(1,
		mDebugPuts("SD状态数据块(SSR)：");
		mDebugPutMemoryForU32(MLOG_SPACE, &ssr[0], 4);
		mDebugPutMemoryForU32(MLOG_SPACE, &ssr[4], 4);
		mDebugPutMemoryForU32(MLOG_SPACE, &ssr[8], 4);
		mDebugPutMemoryForU32(MLOG_SPACE, &ssr[12], 4);
		mDebugPutStr(MLOG_SPACE "[数据总线宽度]：");
		if (SDIO_SSR_DAT_BUS_WIDTH(ssr) == SDIO_SSR_DAT_BUS_WIDTH_1) {
			mDebugPuts("1Bit");
		} else if (SDIO_SSR_DAT_BUS_WIDTH(ssr) == SDIO_SSR_DAT_BUS_WIDTH_4) {
			mDebugPuts("4Bit");
		} else {
			mDebugPuts("未知");
		}
		mDebugPutStr(MLOG_SPACE "[安全模式]：");
		if (SDIO_SSR_SECURED_MODE(ssr) == SDIO_SSR_SECURED_MODE_EN) {
			mDebugPuts("开启");
		} else {
			mDebugPuts("关闭");
		}
		mDebugPutStr(MLOG_SPACE "[卡读写]：");
		if (SDIO_SSR_CARD_TYPE(ssr) == SDIO_SSR_CARD_TYPE_RW) {
			mDebugPuts("可读写");
		} else {
			mDebugPuts("只读");
		}
		mDebugPutStr(MLOG_SPACE "[保护区大小]：");
		if (sdInfo.Type == CARD_TYPE_SDSC) {
			mDebugPrintf("%u" MLOG_ENDL, _SDIO_SsrSdscProtectedArea(
			             SDIO_SSR_SIZE_OF_PROTECTED_AREA(ssr),
			             sdInfo.BlockLen));
		} else {
			mDebugPrintf("%u" MLOG_ENDL, _SDIO_SsrSdhcProtectedArea(
			             SDIO_SSR_SIZE_OF_PROTECTED_AREA(ssr)));
		}
		mDebugPutStr(MLOG_SPACE "[速度等级]：");
		switch (SDIO_SSR_SPEED_CLASS(ssr)) {
			case SDIO_SSR_SPEED_CLASS_0:
				mDebugPuts("CLASS0");
				break;
			case SDIO_SSR_SPEED_CLASS_2:
				mDebugPuts("CLASS2");
				break;
			case SDIO_SSR_SPEED_CLASS_4:
				mDebugPuts("CLASS4");
				break;
			case SDIO_SSR_SPEED_CLASS_6:
				mDebugPuts("CLASS6");
				break;
		}
		mDebugPutStr(MLOG_SPACE "[移动性能]：");
		if (SDIO_SSR_PERFORMANCE_MOVE(ssr) ==
		    SDIO_SSR_PERFORMANCE_MOVE_UNDEF) {
			mDebugPuts("未定义");
		} else if (SDIO_SSR_PERFORMANCE_MOVE(ssr) ==
		           SDIO_SSR_PERFORMANCE_MOVE_INF) {
			mDebugPuts("无穷大");
		} else {
			mDebugPrintf("%dM/s" MLOG_ENDL, SDIO_SSR_PERFORMANCE_MOVE_V(
			             SDIO_SSR_PERFORMANCE_MOVE(ssr)));
		}
		mDebugPrintf(MLOG_SPACE "[AU大小]：%uB" MLOG_ENDL,
		             SDIO_SSR_AU_SIZE_V(SDIO_SSR_AU_SIZE(ssr)));
		mDebugPutStr(MLOG_SPACE "[擦除AU数]：");
		if (SDIO_SSR_ERASE_SIZE(ssr) == SDIO_SSR_ERASE_SIZE_UNDEF) {
			mDebugPutStr("未定义");
		} else {
			mDebugPrintf("%d个，超时%u秒，偏移值%u秒",
			             SDIO_SSR_ERASE_SIZE_V(SDIO_SSR_ERASE_SIZE(ssr)),
			             SDIO_SSR_ERASE_TIMEOUT_V(SDIO_SSR_ERASE_TIMEOUT(ssr)),
			             SDIO_SSR_ERASE_OFFSET_V(SDIO_SSR_ERASE_OFFSET(ssr)));
		}
	);
}

/**
 * 读取一个块的数据，对于标准容量卡，addr是字节地址，对于高容量卡，
 * addr是块编号
 */
static void mSdioCmd_CMD17_READ_SINGLE_BLOCK(void *buf, uint32_t addr)
{
	uint32_t sta;
	uint32_t res;

	/* 接收数据超时时间 */
	SOC_SDIO.DTIMER = 0xFFFFFFFF;
	/* 接收数据的字节长度 */
	SOC_SDIO.DLEN = sdInfo.BlockLen;
	/* 接收控制参数，高容量仅支持按块读取，数据块长度必须是一个块的长度 */
	SOC_SDIO.DCTRL = SDIO_DCTRL_DTEN_EN |       // 使能数据传输
	                 SDIO_DCTRL_DTDIR_IN |      // 数据接收
	                 SDIO_DCTRL_DTMODE_BLOCK |  // 块传输
	                 SDIO_DCTRL_DMAEN_DIS |     // 禁用DMA
	                 sdInfo.DCTRL;

	/* 参数：
	     [31:0]: 地址 */
	SOC_SDIO.ARG = addr;
	/* 编址（点到点）数据传输命令(adtc)，包含数据传输 */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_READ_SINGLE_BLOCK) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);
	/* 等待数据接收完成，期间需要将有效数据读出，否者DCOUNT为0时，
	   SDIO_FLAG_RXACT不会清除 */
	if ((uint32_t)buf & 0x3U) {
		if ((uint32_t)buf & 0x1U) {
			/* 1字节对齐 */
			uint8_t *p = (uint8_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_RXDAVL) {
					val = SOC_SDIO.FIFO[0];
					*p++ = (uint8_t)(val >> 0);
					*p++ = (uint8_t)(val >> 8);
					*p++ = (uint8_t)(val >> 16);
					*p++ = (uint8_t)(val >> 24);
				}
			} while (sta & SDIO_FLAG_RXACT);
			res = (uint32_t)p-(uint32_t)buf;
		} else {
			/* 2字节对齐 */
			uint16_t *p = (uint16_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_RXDAVL) {
					val = SOC_SDIO.FIFO[0];
					*p++ = (uint16_t)(val >> 0);
					*p++ = (uint16_t)(val >> 16);
				}
			} while (sta & SDIO_FLAG_RXACT);
			res = (uint32_t)p-(uint32_t)buf;
		}
	} else {
		/* 4字节对齐 */
		uint32_t *p = (uint32_t *)buf;
		do {
			sta = SOC_SDIO.STA;
			if (sta & SDIO_FLAG_RXDAVL) {
				*p++ = SOC_SDIO.FIFO[0];
			}
		} while (sta & SDIO_FLAG_RXACT);
		res = (uint32_t)p-(uint32_t)buf;
	}
	/* 校验接收数据的长度 */
	if (res != sdInfo.BlockLen) {
		while (1);
	}

	/* 检查响应状态包含：收到响应且CRC正常、数据结束SCOUNT为0、数据已接收
	   且CRC正常、RX FIFO为空 */
	if (sta & (~(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND |
	             SDIO_FLAG_RXFIFOE))) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_READ_SINGLE_BLOCK) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}

	/* 查询卡的状态确保卡已经空闲，通常传输完毕后，卡就空闲了 */
	while (!(res & SDIO_CSR_READY_FOR_DATA)) {
		res = mSdioCmd_CMD13_SEND_STATUS();
		MLOG_DEBUG(1,
			mDebugPrintf("需要使用CMD13轮询SD卡的状态，确保卡一空闲");
		);
	}
}

/**
 * 读取多个块的数据，对于标准容量卡，addr是字节地址，对于高容量卡，
 * addr是块编号，数据传输停止后需要发送CMD12命令停止传输，不发送的话卡
 * 不会响应其他命令
 */
static void mSdioCmd_CMD18_READ_MULT_BLOCK(void *buf, uint32_t addr,
                                           uint32_t blocks)
{
	uint32_t sta;
	uint32_t res;

	/* 接收数据超时时间 */
	SOC_SDIO.DTIMER = 0xFFFFFFFF;
	/* 接收数据的字节长度 */
	SOC_SDIO.DLEN = blocks * sdInfo.BlockLen;
	/* 接收控制参数，高容量仅支持按块读取，数据块长度必须是一个块的长度 */
	SOC_SDIO.DCTRL = SDIO_DCTRL_DTEN_EN |       // 使能数据传输
	                 SDIO_DCTRL_DTDIR_IN |      // 数据接收
	                 SDIO_DCTRL_DTMODE_BLOCK |  // 块传输
	                 SDIO_DCTRL_DMAEN_DIS |     // 禁用DMA
	                 sdInfo.DCTRL;

	/* 参数：
	     [31:0]: 地址 */
	SOC_SDIO.ARG = addr;
	/* 编址（点到点）数据传输命令(adtc)，包含数据传输 */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_READ_MULT_BLOCK) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);
	/* 等待数据接收完成，期间需要将有效数据读出，否者DCOUNT为0时，
	   SDIO_FLAG_RXACT不会清除 */
	if ((uint32_t)buf & 0x3U) {
		if ((uint32_t)buf & 0x1U) {
			/* 1字节对齐 */
			uint8_t *p = (uint8_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_RXDAVL) {
					val = SOC_SDIO.FIFO[0];
					*p++ = (uint8_t)(val >> 0);
					*p++ = (uint8_t)(val >> 8);
					*p++ = (uint8_t)(val >> 16);
					*p++ = (uint8_t)(val >> 24);
				}
			} while (sta & SDIO_FLAG_RXACT);
			res = (uint32_t)p-(uint32_t)buf;
		} else {
			/* 2字节对齐 */
			uint16_t *p = (uint16_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_RXDAVL) {
					val = SOC_SDIO.FIFO[0];
					*p++ = (uint16_t)(val >> 0);
					*p++ = (uint16_t)(val >> 16);
				}
			} while (sta & SDIO_FLAG_RXACT);
			res = (uint32_t)p-(uint32_t)buf;
		}
	} else {
		/* 4字节对齐 */
		uint32_t *p = (uint32_t *)buf;
		do {
			sta = SOC_SDIO.STA;
			if (sta & SDIO_FLAG_RXDAVL) {
				*p++ = SOC_SDIO.FIFO[0];
			}
		} while (sta & SDIO_FLAG_RXACT);
		res = (uint32_t)p-(uint32_t)buf;
	}
	/* 校验接收数据的长度 */
	if (res != sdInfo.BlockLen*blocks) {
		while (1);
	}

	/* 检查响应状态包含：收到响应且CRC正常、数据结束SCOUNT为0、数据已接收
	   且CRC正常、RX FIFO为空 */
	if (sta & (~(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND |
	             SDIO_FLAG_RXFIFOE))) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_READ_MULT_BLOCK) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}

	/* 查询卡的状态确保卡已经空闲，通常传输完毕后，卡就空闲了 */
	while (!(res & SDIO_CSR_READY_FOR_DATA)) {
		res = mSdioCmd_CMD13_SEND_STATUS();
		MLOG_DEBUG(1,
			mDebugPrintf("需要使用CMD13轮询SD卡的状态，确保卡一空闲");
		);
	}
	/* 停止传输 */
	mSdioCmd_CMD12_STOP_TRANSMISSION();
}

/**
 * 停止数据传输
 */
static void mSdioCmd_CMD12_STOP_TRANSMISSION(void)
{
	uint32_t sta;
	uint32_t res;

	/* 无参数 */
	SOC_SDIO.ARG = 0;
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_STOP_TRANSMISSION) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 检查响应状态包含：收到响应且CRC正常 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_STOP_TRANSMISSION) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}

	/* 查询卡的状态确保卡已经空闲，通常多块读时传输完毕后，卡就空闲了。
	   多块写时，写2个块，其中1个块未擦除、1个块已擦除，主频168M，SDIO
	   24M，轮询了351次。此处仅需要判断READY标志，不用关注STATE字段，当
	   READY=1，表示卡空闲之后，STATE将在下一次轮询中恢复到传输状态 */
	while (!(res & SDIO_CSR_READY_FOR_DATA)) {
		res = mSdioCmd_CMD13_SEND_STATUS();
	}
}

/**
 * 设置卡擦除的首地址
 */
static void mSdioCmd_CMD32_SD_ERASE_GRP_START(uint32_t addr)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	     [31:0]: 地址 */
	SOC_SDIO.ARG = addr;
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SD_ERASE_GRP_START) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 检查响应状态包含：收到响应且CRC正常 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SD_ERASE_GRP_START) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
}

/**
 * 设置卡擦除的尾地址
 */
static void mSdioCmd_CMD33_SD_ERASE_GRP_END(uint32_t addr)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	     [31:0]: 地址 */
	SOC_SDIO.ARG = addr;
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SD_ERASE_GRP_END) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 检查响应状态包含：收到响应且CRC正常 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SD_ERASE_GRP_END) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
}

/**
 * 启动卡擦除操作
 */
static void mSdioCmd_CMD38_ERASE(void)
{
	uint32_t sta;
	uint32_t res;

	/* 无参数 */
	SOC_SDIO.ARG = 0;
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_ERASE) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 检查响应状态包含：收到响应且CRC正常 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_ERASE) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}

	/* 查询卡的状态确保卡已经空闲，在168M主频，24M SDIO频率，擦除2个块，
	   此处轮询了679次 */
	while (!(res & SDIO_CSR_READY_FOR_DATA)) {
		res = mSdioCmd_CMD13_SEND_STATUS();
	}
}

/**
 * 查询卡的状态，不同于ACMD13，CMD13主要是获取R1响应，R1就是卡状态寄存器
 * (CSR, Card Status Register)，其中[12:9]记录了卡的状态，在执行擦除和写入
 * 操作后需要轮询该状态以确保操作完成
 */
static uint32_t mSdioCmd_CMD13_SEND_STATUS(void)
{
	uint32_t sta;
	uint32_t res;

	/* 参数：
	     [31:16]: RCA，卡的相对地址，初始化时卡的相对地址为0
	     [15: 0]: 无用 */
	SOC_SDIO.ARG = SDIO_ARG_0_FFFFFFFFH(((uint32_t)sdInfo.RelCardAddr)<<16);
	/* 寻址（点对点）命令(ac) */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_SEND_STATUS) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机

	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);

	/* 响应状态中应当只有收到响应且CRC验证通过 */
	if (sta & (~SDIO_FLAG_CMDREND)) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_SEND_STATUS) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}

	return res;
}

/**
 * 写入一个块的数据，对于标准容量卡，addr是字节地址，对于高容量卡，
 * addr是块编号
 */
static void mSdioCmd_CMD24_WRITE_SINGLE_BLOCK(const void *buf,
                                              uint32_t addr)
{
	uint32_t sta;
	uint32_t res;
	uint32_t i;

	/* 发送数据的字节长度 */
	SOC_SDIO.DLEN = sdInfo.BlockLen;
	/* 发送控制参数，高容量仅支持按块写入，数据块长度必须是一个块的长度 */
	SOC_SDIO.DCTRL = SDIO_DCTRL_DTEN_EN |       // 使能数据传输
	                 SDIO_DCTRL_DTDIR_OUT |     // 数据接收
	                 SDIO_DCTRL_DTMODE_BLOCK |  // 块传输
	                 SDIO_DCTRL_DMAEN_DIS |     // 禁用DMA
	                 sdInfo.DCTRL;

	/* 参数：
	     [31:0]: 地址 */
	SOC_SDIO.ARG = addr;
	/* 编址（点到点）数据传输命令(adtc)，包含数据传输 */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_WRITE_SINGLE_BLOCK) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);
	/* 根据TX FIFO情况填充数据 */
	res = sdInfo.BlockLen / 4;
	if ((uint32_t)buf & 0x3U) {
		if ((uint32_t)buf & 0x1U) {
			/* 1字节对齐 */
			const uint8_t *p = (const uint8_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_TXFIFOHE) {
					for (i = 0; i < 8; i++) {
						val  = p[3]; val <<= 8;
						val |= p[2]; val <<= 8;
						val |= p[1]; val <<= 8;
						val |= p[0];
						SOC_SDIO.FIFO[0] = val;
						p += 4;
					}
					res -= 8;
				}
			} while (res);
		} else {
			/* 2字节对齐 */
			const uint16_t *p = (const uint16_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_TXFIFOHE) {
					for (i = 0; i < 8; i++) {
						val  = p[1]; val <<= 16;
						val |= p[0];
						SOC_SDIO.FIFO[0] = val;
						p += 2;
					}
					res -= 8;
				}
			} while (res);
		}
	} else {
		/* 4字节对齐 */
		const uint32_t *p = (const uint32_t *)buf;
		do {
			sta = SOC_SDIO.STA;
			if (sta & SDIO_FLAG_TXFIFOHE) {
				for (i = 0; i < 8; i++) {
					SOC_SDIO.FIFO[0] = p[i];
				}
				p += 8;
				res -= 8;
			}
		} while (res);
	}
	/* 等待发送完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_TXACT);

	/* 检查响应状态包含：收到响应且CRC正常、数据结束SCOUNT为0、数据发送完毕
	   且CRC正常 */
	if (sta & (~(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND))) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_WRITE_SINGLE_BLOCK) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
	/* 查询卡的状态确保卡已经空闲，通常传输完毕后，卡就空闲了 */
	while (!(res & SDIO_CSR_READY_FOR_DATA)) {
		res = mSdioCmd_CMD13_SEND_STATUS();
		MLOG_DEBUG(1,
			mDebugPrintf("需要使用CMD13轮询SD卡的状态，确保卡一空闲");
		);
	}
}

/**
 * 写入多个块的数据，对于标准容量卡，addr是字节地址，对于高容量卡，
 * addr是块编号，该命令结束后需要执行CMD12命令用于停止数据传输
 */
static void mSdioCmd_CMD25_WRITE_MULT_BLOCK(const void *buf,
                                            uint32_t addr, uint32_t blocks)
{
	uint32_t sta;
	uint32_t res;
	uint32_t i;

	/* 发送数据的字节长度 */
	SOC_SDIO.DLEN = sdInfo.BlockLen * blocks;
	/* 发送控制参数，高容量仅支持按块写入，数据块长度必须是一个块的长度 */
	SOC_SDIO.DCTRL = SDIO_DCTRL_DTEN_EN |       // 使能数据传输
	                 SDIO_DCTRL_DTDIR_OUT |     // 数据接收
	                 SDIO_DCTRL_DTMODE_BLOCK |  // 块传输
	                 SDIO_DCTRL_DMAEN_DIS |     // 禁用DMA
	                 sdInfo.DCTRL;

	/* 参数：
	     [31:0]: 地址 */
	SOC_SDIO.ARG = addr;
	/* 编址（点到点）数据传输命令(adtc)，包含数据传输 */
	SOC_SDIO.CMD = SDIO_CMD_INDEX_0_3FH(SDIO_CMD_WRITE_MULT_BLOCK) |
	               SDIO_CMD_WAITRESP_SHORT | // 短响应
	               SDIO_CMD_CPSMEN_EN;       // 使能命令状态机
	/* 等待命令完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_CMDACT);
	/* 根据TX FIFO情况填充数据 */
	res = sdInfo.BlockLen * blocks / 4;
	if ((uint32_t)buf & 0x3U) {
		if ((uint32_t)buf & 0x1U) {
			/* 1字节对齐 */
			const uint8_t *p = (const uint8_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_TXFIFOHE) {
					for (i = 0; i < 8; i++) {
						val  = p[3]; val <<= 8;
						val |= p[2]; val <<= 8;
						val |= p[1]; val <<= 8;
						val |= p[0];
						SOC_SDIO.FIFO[0] = val;
						p += 4;
					}
					res -= 8;
				}
			} while (res);
		} else {
			/* 2字节对齐 */
			const uint16_t *p = (const uint16_t *)buf;
			uint32_t val;

			do {
				sta = SOC_SDIO.STA;
				if (sta & SDIO_FLAG_TXFIFOHE) {
					for (i = 0; i < 8; i++) {
						val  = p[1]; val <<= 16;
						val |= p[0];
						SOC_SDIO.FIFO[0] = val;
						p += 2;
					}
					res -= 8;
				}
			} while (res);
		}
	} else {
		/* 4字节对齐 */
		const uint32_t *p = (const uint32_t *)buf;
		do {
			sta = SOC_SDIO.STA;
			if (sta & SDIO_FLAG_TXFIFOHE) {
				for (i = 0; i < 8; i++) {
					SOC_SDIO.FIFO[0] = p[i];
				}
				p += 8;
				res -= 8;
			}
		} while (res);
	}
	/* 等待发送完成 */
	do {
		sta = SOC_SDIO.STA;
	} while (sta & SDIO_FLAG_TXACT);

	/* 检查响应状态包含：收到响应且CRC正常、数据结束SCOUNT为0、数据发送完毕
	   且CRC正常 */
	if (sta & (~(SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND))) {
		while (1);
	}
	/* 清除状态 */
	SOC_SDIO.ICR = SDIO_FLAG_CMDREND | SDIO_FLAG_DATAEND | SDIO_FLAG_DBCKEND;

	/* 响应格式为R1，校验命令索引 */
	if (SOC_SDIO.RESPCMD != SDIO_CMD_WRITE_MULT_BLOCK) {
		while (1);
	}
	/* 响应的内容是卡状态寄存器(CSR, Card Status Register) */
	res = SOC_SDIO.RESP1;
	if (res & SDIO_CSR_ERRORBITS) {
		/* SD卡状态有错误 */
		while (1);
	}
	/* 查询卡的状态确保卡已经空闲，通常传输完毕后，卡就空闲了 */
	while (!(res & SDIO_CSR_READY_FOR_DATA)) {
		res = mSdioCmd_CMD13_SEND_STATUS();
		MLOG_DEBUG(1,
			mDebugPrintf("需要使用CMD13轮询SD卡的状态，确保卡一空闲");
		);
	}
	/* 停止传输 */
	mSdioCmd_CMD12_STOP_TRANSMISSION();
}

void mSdioSdInit(void)
{
	/* 激活总线 */
	SOC_SDIO.POWER = SDIO_POWER_UP;
	/* 配置时钟 */
	SOC_SDIO.CLKCR = SDIO_CLKCR_CLKDIV_2_257D(120) | // 卡识别过程最大400KB
	                 SDIO_CLKCR_CLKEN_EN |     // 使能SDIO时钟输出
	                 SDIO_CLKCR_PWRSAV_DIS |   // 禁用节能模式，卡识别的过程
	                                           // 中持续发送时钟
	                 SDIO_CLKCR_BYPASS_DIS |   // 使能CLKDIV分频
	                 SDIO_CLKCR_WIDBUS_1 |     // 卡识别过程需要1bit
	                 SDIO_CLKCR_NEGEDGE_RISE | // 在SDIOCLK上升沿产生SDIO输出
	                 SDIO_CLKCR_HWFC_DIS;      // 禁用硬件流控，开启该功
	                                           // 能可用于避免FIFO的溢出，
	                                           // 但分频值必须大于2，也即
	                                           // 4分频，否者SDIO发送数据
	                                           // 时会出现CRC错误，若想使
	                                           // 用最大频率2分频，则需要
	                                           // 关闭硬件流控
	/* 卡上电后需要大约74个时钟后才能发起传输，这段时间卡在处理电源
	   相关的事情 */
	mBoardDelayMs(1);
	/* 卡的相对地址，CMD55命令会用到，卡复位后相对地址为0 */
	sdInfo.RelCardAddr = 0;
	/* 复位卡 */
	mSdioCmd_CMD0_GO_IDLE_STATE();
	/* 查询卡的匹配电压，仅SD V2.0支持 */
	mSdioCmd_CMD8_HS_SEND_EXT_CSD();
	/* 等待卡就绪并查询卡的容量 */
	mSdioCmd_ACMD41_SD_APP_OP_COND();
	/* 查询卡的CID */
	mSdioCmd_CMD2_ALL_SEND_CID();
	/* 查询卡的相对地址，可多次调用以改变卡的相对地址 */
	mSdioCmd_CMD3_SET_REL_ADDR();
	/* 设置卡的相对地址后，即可恢复最高时钟 */
	SOC_SDIO.CLKCR = (SOC_SDIO.CLKCR & (~SDIO_CLKCR_CLKDIV_MASK)) |
	                 SDIO_CLKCR_CLKDIV_2_257D(2);  // SD卡最大25MB，此处24M
	/* 此时卡识别已结束，卡处于待机模式，可发送
	      CMD3: 更新相对地址
	      CMD4: 编程DSR, Driver Stage Reigster
	      CMD9: 查询CSD, Card Specific Data
	      CMD10: 查询相对地址卡的CID, Card Identification Number
	      CMD7: 切换到传输模式 */
	/* 查询卡的CSD */
	mSdioCmd_CMD9_SEND_CSD();
	/* 选中卡，切换到传输模式 */
	mSdioCmd_CMD7_SEL_DESEL_CARD();
	/* 读取卡的SCR信息，查询卡支持的数据总线宽度 */
	mSdioCmd_SDIO_ACMD51_SD_APP_SEND_SCR();
	/* 设置卡的数据总线位宽为4bit */
	mSdioCmd_ACMD6_APP_SD_SET_BUSWIDTH(CARD_BUS_WIDE_4);
	/* 读取SSR信息 */
	mSdioCmd_SDIO_ACMD13_SD_APP_STATUS();
	/* 设置卡侧块数据传输的长度 */
	mSdioCmd_CMD16_SET_BLOCKLEN(sdInfo.BlockLen);
}

void mSdioSdRead(void *buf, uint32_t start, uint32_t blocks)
{
	if (blocks == 1) {
		mSdioCmd_CMD17_READ_SINGLE_BLOCK(buf, start);
	} else {
		mSdioCmd_CMD18_READ_MULT_BLOCK(buf, start, blocks);
	}
}

void mSdioSdWrite(const void *buf, uint32_t start, uint32_t blocks)
{
	if (blocks == 1) {
		mSdioCmd_CMD25_WRITE_MULT_BLOCK(buf, start, blocks);
	} else {
		mSdioCmd_CMD24_WRITE_SINGLE_BLOCK(buf, start);
	}
}

void mSdioSdCtrl(uint8_t cmd, void *buf)
{
	switch (cmd) {
		case CTRL_SYNC:        // 同步，当文件系统支持写入的时候需要
			break;
		case GET_SECTOR_COUNT: // 获取扇区数，当文件系统支持格式化的时候需要
			*(LBA_t *)buf = sdInfo.Blocks;
			break;
		case GET_SECTOR_SIZE:  // 每个扇区多少字节，当定义磁盘扇区最大和最小不一样的时候需要，用于文件系统格式化、分区和挂载
			*(WORD *)buf = sdInfo.BlockLen;
			break;
		case GET_BLOCK_SIZE:   // 每个块包含多少个扇区，文件系统中块表示擦除的最小单位，当文件系统支持格式化的时候需要
			*(DWORD *)buf = 1;
			break;
		case CTRL_TRIM:        // 用于支持ATA-TRIM，SD卡不需要
			break;
	}
}

static uint32_t msdioTestBuf[1024/4+1];

void mSdioTest(void)
{
	uint8_t *p8 = (uint8_t *)msdioTestBuf;
	uint32_t i;

	mSdioSdInit();
	/* 读测试 */
	mDebugPuts("单次读取1个块数据：");
	memset(p8, 0xAA, sizeof(msdioTestBuf));
	mSdioCmd_CMD17_READ_SINGLE_BLOCK(p8, 0);
	mDebugHexDumpU8(p8, sdInfo.BlockLen, 32, "%04x: ", 0);
	memset(p8, 0xAA, sizeof(msdioTestBuf));
	mDebugPuts("单次读取2个块数据：");
	mSdioCmd_CMD18_READ_MULT_BLOCK(p8, 0, 2);
	mDebugHexDumpU8(p8, sdInfo.BlockLen*2, 32, "%04x: ", 0);
	memset(p8, 0xAA, sizeof(msdioTestBuf));
	mDebugPuts("单次读取1个块数据：");
	mSdioCmd_CMD17_READ_SINGLE_BLOCK(&p8[1], 0);
	mDebugHexDumpU8(&p8[1], sdInfo.BlockLen, 32, "%04x: ", 0);
	memset(p8, 0xAA, sizeof(msdioTestBuf));
	mDebugPuts("单次读取2个块数据：");
	mSdioCmd_CMD18_READ_MULT_BLOCK(&p8[2], 0, 2);
	mDebugHexDumpU8(&p8[2], sdInfo.BlockLen*2, 32, "%04x: ", 0);

	/* 擦除测试 */
	mDebugPuts("擦除后数据：");
	mSdioCmd_CMD32_SD_ERASE_GRP_START(0);
	mSdioCmd_CMD33_SD_ERASE_GRP_END(1);
	mSdioCmd_CMD38_ERASE();
	mSdioCmd_CMD18_READ_MULT_BLOCK(&p8[3], 0, 2);
	mDebugHexDumpU8(&p8[3], sdInfo.BlockLen*2, 32, "%04x: ", 0);

	/* 写入测试 */
	for (i = 0; i < sdInfo.BlockLen; i++) {
		p8[i+1] = i;
	}
	mDebugPuts("写入数据：");
	mSdioCmd_CMD24_WRITE_SINGLE_BLOCK(&p8[1], 0);
	memset(p8, 0xAA, sizeof(msdioTestBuf));
	mSdioCmd_CMD17_READ_SINGLE_BLOCK(&p8[2], 0);
	for (i = 0; i < sdInfo.BlockLen; i++) {
		if (p8[i+2] != (i&0xFF)) {
			mDebugPrintf("写入地址%04x的值为: %02x，读出为: %02x" MLOG_ENDL,
			             i, i, p8[i+2]);
		}
	}
	for (i = 0; i < sdInfo.BlockLen/2; i++) {
		p8[i+2] = 0x00;
	}
	for (; i < sdInfo.BlockLen; i++) {
		p8[i+2] = 0xff;
	}
	for (; i < sdInfo.BlockLen*2; i++) {
		p8[i+2] = i;
	}
	mSdioCmd_CMD25_WRITE_MULT_BLOCK(&p8[2], 0, 2);
	mSdioCmd_CMD17_READ_SINGLE_BLOCK(&p8[2], 1);
	for (i = sdInfo.BlockLen; i < sdInfo.BlockLen*2; i++) {
		if (p8[i+2] != (i&0xFF)) {
			mDebugPrintf("写入地址%04x的值为: %02x，读出为: %02x" MLOG_ENDL,
			             i, i, p8[i+2]);
		}
	}
	mDebugPuts("未擦除后写入：");
	mSdioCmd_CMD17_READ_SINGLE_BLOCK(p8, 0);
	mDebugHexDumpU8(p8, sdInfo.BlockLen, 32, "%04x: ", 0);
	/* buf 4字节对齐写入 */
	for (i = 0; i < sdInfo.BlockLen; i++) {
		p8[i] = i;
	}
	mSdioCmd_CMD24_WRITE_SINGLE_BLOCK(p8, 0);
	memset(p8, 0xAA, sizeof(msdioTestBuf));
	mSdioCmd_CMD17_READ_SINGLE_BLOCK(&p8[6], 0);
	for (i = 0; i < sdInfo.BlockLen; i++) {
		if (p8[i+6] != (i&0xFF)) {
			mDebugPrintf("写入地址%04x的值为: %02x，读出为: %02x" MLOG_ENDL,
			             i, i, p8[i+6]);
		}
	}
	mDebugPuts("测试结束");
}
